在Spring Security 2.06中实现自定义AuthenticationProvider [英] Implement custom AuthenticationProvider in Spring Security 2.06

查看:662
本文介绍了在Spring Security 2.06中实现自定义AuthenticationProvider的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Spring Security来保护Struts2 Web应用程序。由于项目限制,我使用的是Spring Security 2.06。

I'm using Spring Security to secure a Struts2 web application. Due to project constraints, I'm using Spring Security 2.06.

我的团队构建了一个自定义用户管理API,用于在接收用户名和密码参数后对用户进行身份验证,并返回包含角色列表和其他属性(如电子邮件,名称等)的自定义用户对象。

My team built a custom User Management API that authenticates a user after taking in username and password parameters, and returns a custom user object containing a list of roles and other attributes like email, name, etc.

根据我的理解,典型的Spring Security用例使用默认的UserDetailsS​​ervice来检索UserDetails对象;此对象将包含(以及其他内容)框架将用于对用户进行身份验证的密码字段。

From my understanding, the typical Spring Security use-case uses a default UserDetailsService to retrieve a UserDetails object; this object will contain (among other things) a password field that will be used by the framework to authenticate the user.

在我的情况下,我想让我们的自定义API进行身份验证,然后返回一个包含角色和其他属性的自定义UserDetails对象(电子邮件等)。

In my case, I want to let our custom API do the authentication, then return a custom UserDetails object containing the roles and other attributes (email, etc).

经过一番研究,我发现我可以通过AuthenticationProvider的自定义实现。我也有UserDetailsS​​ervice和UserDetails的自定义实现。

After some research, I figured out that I can do this through a custom implementation of AuthenticationProvider. I also have custom implementations of UserDetailsService and UserDetails.

我的问题是我真的不明白我应该在CustomAuthenticationProvider中返回什么。我在这里使用我的自定义UserDetailsS​​ervice对象吗?这甚至需要吗?对不起,我真的很困惑。

My problem is that I don't really understand what I'm supposed to be returning in CustomAuthenticationProvider. Do I use my custom UserDetailsService object here? Is that even needed? Sorry, I'm really confused.

CustomAuthenticationProvider:

CustomAuthenticationProvider:

public class CustomAuthenticationProvider implements AuthenticationProvider {

private Logger logger = Logger.getLogger(CustomAuthenticationProvider.class);

private UserDetailsService userDetailsService; //what am i supposed to do with this?

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
    String username = String.valueOf(auth.getPrincipal());
    String password = String.valueOf(auth.getCredentials());

    logger.info("username:" + username);
    logger.info("password:" + password);
    /* what should happen here? */

    return null;  //what do i return?
}

@Override
public boolean supports(Class aClass) {
    return true;  //To indicate that this authenticationprovider can handle the auth request. since there's currently only one way of logging in, always return true
}

public UserDetailsService getUserDetailsService() {
    return userDetailsService;
}

public void setUserDetailsService(UserDetailsService userDetailsService) {
    this.userDetailsService = userDetailsService;
}

}

applicationContext-security.xml:

applicationContext-security.xml:

<beans:bean id="customUserDetailsService" scope="prototype" class="com.test.testconsole.security.CustomUserDetailsService"/>

<beans:bean id="customAuthenticationProvider" class="com.test.testconsole.security.CustomAuthenticationProvider">
    <custom-authentication-provider />
    <beans:property name="userDetailsService" ref="customUserDetailsService" />
</beans:bean>

总结一下,这就是我需要的:

To summarize, this is what I need:


  1. 用户通过网络表单登录

  2. 使用内部用户管理API验证用户

  3. 成功经过身份验证的用户,填充GrantedAuthories等。

  4. 返回包含角色/权限的用户实体,以及电子邮件,名称等其他属性。然后,我应该能够访问此对象喜欢这样..

  1. User logs in through a web form
  2. Authenticate user using in-house user management API
  3. For successfully authenticated users, populate GrantedAuthories, etc.
  4. Return an user entity containing roles/authorities, and other attributes like email, name, etc. I should then be able to access this object like so ..

//spring security get user name
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
userName = auth.getName(); //get logged in username
logger.info("username: " + userName);

//spring security get user role
GrantedAuthority[] authorities = auth.getAuthorities();
userRole = authorities[0].getAuthority();
logger.info("user role: " + userRole);


我希望这是有道理的。任何帮助或指示将不胜感激!

I hope this makes sense. Any help or pointers will be appreciated!

谢谢!

更新:

我认为我取得了一些进展。

I've made some progress, I think.

我有一个实现身份验证界面的自定义身份验证对象:

I have a custom Authentication object implementing the Authentication interface:

public class CustomAuthentication implements Authentication {

    String name;
    GrantedAuthority[] authorities;
    Object credentials;
    Object details;
    Object principal;
    boolean authenticated;

    public CustomAuthentication(String name, GrantedAuthority[] authorities, Object credentials, Object details, Object principal, boolean
                                authenticated){
        this.name=name;
        this.authorities=authorities;
        this.details=details;
        this.principal=principal;
        this.authenticated=authenticated;

    }
    @Override
    public GrantedAuthority[] getAuthorities() {
        return new GrantedAuthority[0];  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Object getCredentials() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Object getDetails() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Object getPrincipal() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public boolean isAuthenticated() {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public String getName() {
        return null;  
    }
}

并更新了我的CustomerAuthenticationProvider类:

and updated my CustomerAuthenticationProvider class:

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
        String username = String.valueOf(auth.getPrincipal());
        String password = String.valueOf(auth.getCredentials());

        logger.info("username:" + username);
        logger.info("password:" + password);

        //no actual validation done at this time

        GrantedAuthority[] authorities = new GrantedAuthorityImpl[1];
        authorities[0] = new GrantedAuthorityImpl("ROLE_USER");

        CustomAuthentication customAuthentication = new CustomAuthentication("TestMerchant",authorities,"details",username,password,true);

    return customAuthentication;

    //return new UsernamePasswordAuthenticationToken(username,password,authorities); 
}

如果我返回UsernamePasswordAuthenticationToken对象,但是如果我尝试返回CustomAuthentication,则它有效,我收到以下错误:

It works if I return an UsernamePasswordAuthenticationToken object, but if I try to return CustomAuthentication, I get the following error:

java.lang.ClassCastException: com.test.testconsole.security.CustomAuthentication cannot be cast to org.springframework.security.providers.UsernamePasswordAuthenticationToken
    at com.test.testconsole.security.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:27)
    at org.springframework.security.providers.ProviderManager.doAuthentication(ProviderManager.java:188)
    at org.springframework.security.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:46)
    at org.springframework.security.intercept.AbstractSecurityInterceptor.authenticateIfRequired(AbstractSecurityInterceptor.java:319)
    at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:258)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.SessionFixationProtectionFilter.doFilterHttp(SessionFixationProtectionFilter.java:67)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:116)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.basicauth.BasicProcessingFilter.doFilterHttp(BasicProcessingFilter.java:174)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:278)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:236)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
    at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:536)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:915)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:539)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:405)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

这就像某些东西不仅仅期望任何Authentication对象,而是它的特定实现 - UsernamePasswordAuthenticationToken。这让我觉得我可能会错过另一个自定义组件..也许是一个过滤器?

It's like something is expecting not just any Authentication object, but a specific implementation of it - UsernamePasswordAuthenticationToken. This makes me think that I may be missing another custom component .. maybe a filter?

推荐答案

如果你正在实现自己的 AuthenticationProvider ,如果你不想,你不必实现 UserDetailsS​​ervice UserDetailsS​​ervice 只提供用于加载用户信息的标准DAO,并且框架内的一些其他类被实现为使用它。

If you are implementing your own AuthenticationProvider, You don't have to implement a UserDetailsService if you don't want to. UserDetailsService just provides a standard DAO for loading user information and some other classes within the framework are implemented to use it.

通常,要使用用户名和密码进行身份验证,您需要实例化 DaoAuthenticationProvider 并使用 UserDetailsS​​ervice 注入。这可能仍然是你最好的方法。如果您实施自己的提供商,则您负责确保用户提供了正确的密码等。但是,在某些情况下,这是一种更简单的方法。

Normally, to authenticate using a username and password, you would instantiate a DaoAuthenticationProvider and inject that with a UserDetailsService. That may still be your best approach. If you implement your own provider, you take on the responsibility of making sure the user has supplied the correct password and so on. However, in some cases this is a simpler approach.

回答你的这里应该发生什么?在您的代码中注释,它将类似于

To answer your "what should happen here?" comment in your code, it would be something like

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
  String username = String.valueOf(auth.getPrincipal());
  String password = String.valueOf(auth.getCredentials());

  logger.info("username:" + username);
  logger.info("password:" + password); // Don't log passwords in real app

  // 1. Use the username to load the data for the user, including authorities and password.
  YourUser user = ....

  // 2. Check the passwords match (should use a hashed password here).
  if (!user.getPassword().equals(password)) {
    throw new BadCredentialsException("Bad Credentials");
  }

  // 3. Preferably clear the password in the user object before storing in authentication object
  user.clearPassword();

  // 4. Return an authenticated token, containing user data and authorities  

  return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()) ;
}

然后可以使用

Authentication.getPrincipal()

方法,您可以通过将其附加到自定义用户实现来访问其他属性(电子邮件等)。

method, and you can access the additional properties (email etc) by casting it to your custom user implementation.

如何加载用户数据取决于您。所有Spring Security关心的都是 AuthenticationProvider 界面。

How you load the user data is up to you. All Spring Security cares about here is the AuthenticationProvider interface.

您还应该存储散列密码并验证提供的密码使用相同的算法,而不是简单的相等检查。

You should also store hashed passwords and validate the supplied password using the same algorithm, rather than a simple equality check.

这篇关于在Spring Security 2.06中实现自定义AuthenticationProvider的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆