[英]Spring security - remember-me authentication with multiple authentication providers
我在我的Web应用程序中使用Spring security 3.2.0。
我有两个安全角色:具有存储在数据库中的凭据的用户和具有存储在配置文件中的凭据的管理员。
它工作正常,直到我打开记住我身份验证。 这是我的security.xml
配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<security:global-method-security secured-annotations="enabled"/>
<security:http auto-config="true">
<security:remember-me key="myAppKey" user-service-ref="adminSrv"/>
<security:remember-me key="myAppKey" user-service-ref="jdbcUserSrv"/>
<security:form-login login-page="/login" default-target-url="/"
authentication-failure-url="/loginfailed" />
<security:logout logout-success-url="/logout"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource" id="jdbcUserSrv"
users-by-username-query="/* SQL query */"
authorities-by-username-query="/* Another SQL query */"
/>
</security:authentication-provider>
<security:authentication-provider>
<security:user-service id="adminSrv">
<security:user name="admin" authorities="ROLE_ADMIN" password="passwd"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
当我以管理员身份登录时,它就可以了。
当我尝试以用户身份登录时,它会在j_spring_security_check上抛出异常:
HTTP ERROR 500
Problem accessing /mercury/j_spring_security_check. Reason:
user1
Caused by:
org.springframework.security.core.userdetails.UsernameNotFoundException: user1
at org.springframework.security.provisioning.InMemoryUserDetailsManager.loadUserByUsername(InMemoryUserDetailsManager.java:113)
at org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.onLoginSuccess(TokenBasedRememberMeServices.java:178)
at org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices.loginSuccess(AbstractRememberMeServices.java:262)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:324)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:298)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:235)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
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:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
但是,如果它已成功通过身份验证,并且如果我打开我的应用程序索引页面,则会显示为“user1”。
如果remember-me
的jdbcUserSrv配置在adminSrv的配置之前,那么“admin”用户会出现错误。
当前配置工作正常没有remember-me
选项,但我需要启用它们。
请告诉我如何避免这些错误。
拥有两个记住我的元素简直就是相互重叠。 拥有多个UserDetailsService实例也会让您遇到麻烦。 例如,我假设您可能有与您的用户相关的数据。 如果内存和jdbc UserDetailsService实例中都存在名为admin的用户,那么您如何知道该数据属于哪个管理员用户? 因此,最好使用单个UserDetailsService。
或者,您可以创建一个委托给多个UserDetailsService实例的UserDetailsService。 例如:
public class DelegatingUserDetailsService implements UserDetailsService {
private final List<UserDetailsService> userDetailsServices;
public DelegatingUserDetailsService(
List<UserDetailsService> userDetailsServices) {
this.userDetailsServices = userDetailsServices;
}
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
RuntimeException last = null;
for(UserDetailsService uds : userDetailsServices) {
try {
return uds.loadUserByUsername(username);
} catch(RuntimeException error) {
last = error;
}
}
throw last;
}
}
然后你可以记住 - 我用它代替:
<bean id="delegateUds" class="DelegatingUserDetailsService">
<constructor-arg>
<list>
<ref bean="jdbcUserSrv"/>
<ref bean="adminSrv"/>
</list>
</constructor-arg>
</bean>
<security:http auto-config="true">
<security:remember-me key="myAppKey" user-service-ref="delegateUds"/>
<security:form-login login-page="/login" default-target-url="/"
authentication-failure-url="/loginfailed" />
<security:logout logout-success-url="/logout"/>
</security:http>
如果您使用Spring Security Java Configuration(Spring 5.0.6),请记住我
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//... nothingImportantToThisCase
.rememberMe();
}
和这样的多个身份验证提供程序:
public void configureGlobal(AuthenticationManagerBuilder auth,
UserDetailsService userDetailsService) Exception {
auth
.inMemoryAuthentication()
.withUser("ADMIN")
.password("{noopassword")
.roles("ADMIN", "USER")
.and()
.withUser("USER")
.password("{noop}password)
.roles("USER");
auth
.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
将抛出相同的异常,但仍将登录。但令人惊讶的是,这将起作用:
@Autowired
@Order(1)
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("ADMIN")
.password("{noop}password")
.roles("ADMIN", "USER")
.and()
.withUser("USER")
.password("{noop}password")
.roles("USER");
}
@Autowired
@Order(2)
public void configureGlobal(AuthenticationManagerBuilder auth,
UserDetailsService userDetailsService) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
重要的是,即使他们有不同的方法参数,如果只需要一个必须有@Autowired
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.