2012-02-29 87 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