在 Spring Security 2.06 中实现自定义身份验证提供程序
我正在使用Spring Security来保护Struts2 Web应用程序。由于项目限制,我使用的是Spring Security 2.06。
我的团队构建了一个自定义用户管理 API,该 API 在获取用户名和密码参数后对用户进行身份验证,并返回一个自定义用户对象,其中包含角色列表和其他属性(如电子邮件、名称等)。
根据我的理解,典型的Spring Security用例使用默认的UserDetailsService来检索UserDetails对象;此对象将包含(除其他事项外)框架将用于对用户进行身份验证的密码字段。
在我的情况下,我想让我们的自定义API进行身份验证,然后返回一个包含角色和其他属性(电子邮件等)的自定义UserDetails对象。
经过一些研究,我发现我可以通过身份验证提供程序的自定义实现来做到这一点。我还有UserDetailsService和UserDetails的自定义实现。
我的问题是,我真的不明白我应该在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;
}
}
应用程序上下文安全.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>
总而言之,这就是我需要的:
- 用户通过网络表单登录
- 使用内部用户管理 API 对用户进行身份验证
- 对于成功通过身份验证的用户,请填充授权授权机构等。
-
返回包含角色/权限和其他属性(如电子邮件、姓名等)的用户实体。然后,我应该能够像这样访问此对象..
//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);
我希望这是有道理的。任何帮助或指导将不胜感激!
谢谢!
更新:
我想,我已经取得了一些进展。
我有一个实现身份验证接口的自定义身份验证对象:
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;
}
}
并更新了我的客户身份验证提供程序类:
@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,我得到以下错误:
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)
这就像某些东西不仅期望任何身份验证对象,而且期望它的特定实现 - UsernamePasswordAuthenticationToken。这让我觉得我可能缺少另一个自定义组件。也许是过滤器?