2012-02-29 88 views
1

我想以编程方式实现Jetty服务器的基本认证,如所示。为了方便起见,我在这里写下这段代码。骆驼码头的基本认证

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(); 

现在的问题是,上述方法需要你有一个处理服务器。但在我的情况下,因为我使用骆驼,我没有直接访问服务器。这是我的管道是如何定义的:

from("jetty:http://localhost:8080/documents_in?matchOnUriPrefix=true"). 
    process(new MyProcessor()); 

如何适应链接的身份验证解决方案,以我的情况?还是我必须遵循一些完全不同的方法?

请注意,我是骆驼和码头新手。任何帮助将不胜感激。谢谢。

附录:

This page展示了如何使用Spring XML做到这一点,但我们不使用Spring,所以这是没有用的我们。

回答

0

Camel中的JettyComponent具有getter/setter,您可以在其中使用Java代码进行配置。

JettyComponent jetty = new JettyComponent(); 
// use getter/setter to configure 
// add component to Camel 
camelContext.addComponent("jetty", jetty); 
// after this you can add the routes and whatnot 
1

我碰到这个问题前两天偶然和我通过定义自己的实现ConstraintSecurityHandler的它采用了定制的login服务这需要认证BasicAuthenticator需要照顾解决了这个问题。由于我没有找到任何能够处理bean管理认证的现有LoginService实现,所以我需要提出这个解决方案。

我会发布几乎完整的课程,除了必须保持私密的内部东西。

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) 
     { 

     } 
    } 
} 

对此处理程序添加到骆驼的嵌入式Jetty服务器,您可以使用此处理程序是这样定义端点:

jetty:https://your-server:port/api/v1/yourService?sslContextParameters=#sslContextParameters&handlers=#jettyAuthHandler 

其中jettyAuthHandler是这样处理的bean的名字 - 如果你不使用SSL只是省略了sslContextParameters参数。

+0

感谢罗马分享这个。如果我使用您的代码来测试我的骆驼http功能,可以吗?我有运行使用camel-http进行出站HTTP调用的环境。当我们计划迁移到camel-http4时,我希望有足够的测试案例来体验真实的现场环境。对于测试用例,我使用基本身份验证(使用您的代码)托管Jetty,并在测试用例中运行我们的出站呼叫。如果您在使用代码时有任何保留,请告诉我们。 – Robin 2017-01-10 18:07:56

+0

@Robin随意使用代码,只要你喜欢。当我发布代码时,SO在帖子上保留了CC-BY-SA许可证,但现在更改为[MIT许可证](http://meta.stackexchange.com/questions/271080/the-mit-license-clarity-上使用码-上堆栈上溢和堆栈交换)。因此,我认为我所有的旧帖子也是麻省理工学院(不知道我是否有权这样做^^) – 2017-01-10 19:01:24

+0

非常感谢。我已经添加了MIT许可证(即使代码仅用于测试)。此外,您的代码甚至提供了使用Jetty :: JAAS库验证X509证书的登录服务。让我看看是否可以用骆驼码头运行。 – Robin 2017-01-11 19:58:24