java - 使用 Camel Jetty 进行基本身份验证

标签 java jetty basic-authentication apache-camel

我想以编程方式实现 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/

相关文章:

java - 检查字符串是否包含特定字符的最有效方法

java - Jetty 显示 http 错误消息 503 SERVICE_UNAVAILABLE

spring-security - Spring OAuth2 授权服务器,带有通过基本身份验证保护的执行器端点

ruby-on-rails - Rails - authenticate_or_request_with_http_basic 自定义 "access denied"消息

java - 有没有像adobe acrobat这样可以生成pdf格式的图书馆?

java - 如何使用 JAX-WS 客户端指定 WS-Addressing 版本?

java - Jetty 重定向和上下文路径

jquery 跨域认证

c# - 如何使用 VS 调试服务器实现基本的 HTTP 身份验证?

java - 抽象类是否隐藏或重写了接口(interface)的方法?