我想以编程方式实现 Jetty 服务器的基本身份验证,如图 here 。为了方便起见,我在这里^C-^V'ing该片段。
import org.mortbay.jetty.security.*;
Server server = new Server();
Connector connector = new SelectChannelConnector();
connector.setPort(8080);
server.setConnectors(new Connector[]{connector});
Constraint constraint = new Constraint();
constraint.setName(Constraint.__BASIC_AUTH);;
constraint.setRoles(new String[]{"user","admin","moderator"});
constraint.setAuthenticate(true);
ConstraintMapping cm = new ConstraintMapping();
cm.setConstraint(constraint);
cm.setPathSpec("/*");
SecurityHandler sh = new SecurityHandler();
sh.setUserRealm(new HashUserRealm("MyRealm",System.getProperty("jetty.home")+"/etc/realm.properties"));
sh.setConstraintMappings(new ConstraintMapping[]{cm});
WebAppContext webappcontext = new WebAppContext();
webappcontext.setContextPath("/mywebapp");
webappcontext.setWar("./path/to/my/war/orExplodedwar");
webappcontext.addHandler(sh);
HandlerCollection handlers= new HandlerCollection();
handlers.setHandlers(new Handler[]{webappcontext, new DefaultHandler()});
server.setHandler(handlers);
server.start();
server.join();
现在的问题是,上述方法要求您拥有服务器的句柄。但就我而言,由于我使用的是 Camel,所以我无法直接访问服务器。这就是我的管道的定义方式:
from("jetty:http://localhost:8080/documents_in?matchOnUriPrefix=true").
process(new MyProcessor());
如何使链接身份验证解决方案适应我的情况?或者我必须遵循一些完全不同的方法?
请注意,我既是 Camel 又是 Jetty 新手。任何帮助将不胜感激。谢谢。
附录:
This page展示了如何使用 Spring XML 来做到这一点,但是我们没有使用 Spring,所以这对我们来说没有用。
最佳答案
几天前我偶然发现了这个问题,我通过定义自己的 ConstraintSecurityHandler 实现解决了这个问题,该实现使用自定义的 LoginService 来处理 BasicAuthenticator 所需的身份验证。由于我没有找到任何能够处理 bean 管理身份验证的现有 LoginService 实现,因此我需要提出这个解决方案。
除了必须保密的内部内容之外,我将发布几乎完整的类(class)。
import java.security.Principal;
import javax.annotation.Resource;
import javax.security.auth.Subject;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.DefaultIdentityService;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.security.MappedLoginService;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Credential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;
/**
* <p>
* Sets up a basic authentication mechanism for REST based services exposed via
* Jetty for our REST API (http(s)://server:port/api/v1/...).
* </p>
* <p>
* It moreover defines a login service which is capable of using an internal
* persistence layer for authenticating a user and his credentials received via
* a challenge response against a user entity retrieved via the persistence
* layer.
* </p>
*/
public class JettyBasicAuthAuthorizationHandler extends ConstraintSecurityHandler
{
/** The logger of this class **/
private static final Logger logger =
LoggerFactory.getLogger(JettyBasicAuthAuthorizationHandler.class);
/** The persistence service to retrieve the user informations from **/
@Resource
private ISomePersistenceService persistenceService;
private final String[] roles = new String[] {"user"};
/**
* <p>
* Initializes a Jetty based Basic Authentication mechanism.
* </p>
*/
public JettyBasicAuthAuthorizationHandler()
{
super();
// Specifies the challenge to be of type BASIC and that users have
// to fulfill one of the roles listed in roles. Moreover authentication
// is required
Constraint constraint = new Constraint();
constraint.setName(Constraint.__BASIC_AUTH);
constraint.setRoles(this.roles);
constraint.setAuthenticate(true);
// Map the defined constraints from above to the services provided via
// our REST API
ConstraintMapping cm = new ConstraintMapping();
cm.setConstraint(constraint);
cm.setPathSpec("/api/v1/*");
// BasicAuthenticator takes care of sending a challenge to the caller
// and calls our login service in case of a challenge response to
// evaluate if the user is permitted to use the service.
// The realm name defines the name of the login service which should be
// used for authentication.
BasicAuthenticator basic = new BasicAuthenticator();
this.setAuthenticator(basic);
this.addConstraintMapping(cm);
this.setRealmName("REST");
this.setLoginService(new BeanManagedLoginService("REST"));
logger.debug("JettyBasicAuthAuthorizationHandler created!");
}
/**
* <p>
* Implements a bean managed login service where an authentication response
* is propagated to a business layer bean which retrieves the user and
* credentials from a backing data store.
* </p>
*/
class BeanManagedLoginService implements LoginService
{
/** An identity service used to create a UserIdentity object for us **/
private IdentityService identityService = new DefaultIdentityService();
private String name = "REST";
/**
* <p>
* Initializes a new instance.
* </p>
*/
public BeanManagedLoginService()
{
}
/**
* <p>
* Initializes a new instance and sets the realm name this login service
* will work for.
* </p>
*
* @param name The name of this login service (also known as the realm it
* will work for)
*/
public BeanManagedLoginService(String name)
{
this.name = name;
}
/**
* <p>
* Returns the name of the login service (the realm name)
* </p>
*
* @return Get the name of the login service (aka Realm name)
*/
@Override
public String getName()
{
return this.name;
}
/**
* <p>
* Logs in a user by checking the username with known users and
* comparing the credentials with the stored ones. If the user could not
* be authenticated successfully an unauthenticated user identity will
* be returned.
* </p>
*
* @param username The user name as sent by the ChallengeResponse
* @param credentials The credentials provided in the ChallengeResponse
*
* @return If the user could be authenticated successfully a valid
* {@link UserIdentity}, else an unauthorized user identity
*/
@Override
public UserIdentity login(String username, Object credentials)
{
if (logger.isDebugEnabled())
logger.debug("received login request for user: '{}' with credentials: '{}'!",
username, credentials);
// check if the username is valid
if (!Strings.isNullOrEmpty(username))
{
String password = credentials.toString();
// retrieve the user from the business layer
final UserEntity sue = persistenceService.findUser(username);
if (sue == null)
{
if (logger.isErrorEnabled())
logger.error("No User could be found for UserId '{}'. The UserKey (which was not checked) is '{}'",
username, password);
return UserIdentity.UNAUTHENTICATED_IDENTITY;
}
// check whether the password matches the one in the user entity
// found for the user id
if (password.equals(sue.getUserKey()))
{
// the user could be successfully authenticated
if (logger.isDebugEnabled())
logger.debug("UserKey {} of User {} was successfully authenticated",
sue.getUserKey(), sue.getUserId());
if (logger.isDebugEnabled())
logger.debug("User '{}'/'{}' works for '{}'",
userId, userName, sue.getCompany().getName());
return this.createIdentityForUser(username, password);
}
else
{
// the password set in the request and the one stored in the
// user entity do not match
if (logger.isErrorEnabled())
logger.error(
"User {} could not be authenticated. The UserKey in the user entity is {} but the UserKey in the request was {}",
new Object[] { username, sue.getUserKey(), password });
return UserIdentity.UNAUTHENTICATED_IDENTITY;
}
}
else
{
if (logger.isErrorEnabled())
logger.error("Username is empty and therefore could not get authenticated correctly");
return UserIdentity.UNAUTHENTICATED_IDENTITY;
}
}
/**
* <p>
* Creates a UserIdentity object for a successfully authenticated user.
* </p>
*
* @param username The name of the authenticated user
* @param password The password of the authenticated user
*
* @return A valid UserIdentity object
*/
private UserIdentity createIdentityForUser(String username, String password)
{
// create a principal object needed for the user identity
Credential cred = Credential.getCredential(password);
// a principal is basically an identity of a real person
// (subject). So a user can have multiple principals
Principal userPrincipal = new MappedLoginService.KnownUser(username, cred);
// a subject collects all data necessary to identify a certain
// person. It may store multiple identities and passwords or
// cryptographic keys
Subject subject = new Subject();
// add a Principal and credential to the Subject
subject.getPrincipals().add(userPrincipal);
subject.getPrivateCredentials().add(cred);
subject.setReadOnly();
return this.identityService.newUserIdentity(subject, userPrincipal, roles);
}
/**
* <p>
* Validate just checks if a user identity is still valid.
* </p>
*/
@Override
public boolean validate(UserIdentity user)
{
return true;
}
@Override
public IdentityService getIdentityService()
{
return this.identityService;
}
@Override
public void setIdentityService(IdentityService service)
{
this.identityService = service;
}
@Override
public void logout(UserIdentity user)
{
}
}
}
要将此处理程序添加到 Camel 的嵌入式 Jetty 服务器,您可以使用此处理程序定义一个端点,如下所示:
jetty:https://your-server:port/api/v1/yourService?sslContextParameters=#sslContextParameters&handlers=#jettyAuthHandler
其中 jettyAuthHandler
是该处理程序的 bean 名称 - 如果您不使用 SSL,只需省略 sslContextParameters
参数。
关于java - 使用 Camel Jetty 进行基本身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9495858/