【问题标题】:Spring security - remember-me authentication with multiple authentication providersSpring security - 使用多个身份验证提供程序进行记住我的身份验证
【发布时间】:2014-03-04 09:27:39
【问题描述】:

我在我的 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”登录。

如果 jdbcUserSrv 的 remember-me 配置先于 adminSrv 的配置,则“admin”用户会出现错误。

当前配置在没有 remember-me 选项的情况下可以正常工作,但我需要启用它们。

请告诉我如何避免这些错误。

【问题讨论】:

标签: java spring authentication spring-security remember-me


【解决方案1】:

拥有两个记住我的元素只是相互覆盖。拥有多个 UserDetailsS​​ervice 实例也会给您带来麻烦。例如,我假设您可能有与您的用户相关联的数据。如果内存中和 jdbc UserDetailsS​​ervice 实例中都存在名为 admin 的用户,那么您如何知道数据属于哪个 admin 用户?因此,最好使用单个 UserDetailsS​​ervice。

或者,您可以创建一个委托给多个 UserDetailsS​​ervice 实例的 UserDetailsS​​ervice。例如:

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>

【讨论】:

  • 感谢您的回复。我决定实现自己的 UserDetailsS​​ervice,但您的解决方案也非常有用。
【解决方案2】:

如果你使用 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

【讨论】:

    猜你喜欢
    • 2012-02-18
    • 2023-03-05
    • 1970-01-01
    • 2012-03-07
    • 2021-02-28
    • 2014-06-09
    • 2018-05-07
    • 2020-11-09
    • 2016-05-23
    相关资源
    最近更新 更多