Spring security with multiple authentication provi

2019-08-12 17:43发布

问题:

We have a spring security configuration for 2 authentication providers: One for human users, second for other webapps (via REST). See my previous question: Spring security for both web services and users

Now, the problem is, when a webapp sends username + pass, spring security first tries the user authentication provider, fails, and then tries the rest authentication provider.

This causes a org.springframework.security.core.userdetails.UsernameNotFoundException in the log, even if the rest authentication is eventually successful. Is there a way to prevent this from happening?

The configuration is:

security.xml:

<security:http use-expressions="true">
    <security:intercept-url pattern="/user/login"
        access="permitAll" />
             ...
    <security:intercept-url pattern="/**"
        access="isAuthenticated()" />

    <security:form-login
        authentication-success-handler-ref="userAuthenticationSuccessHandler" />

    <security:logout logout-url="/user/logout"
        logout-success-url="/demo/user/logoutSuccess" />
</security:http>

<bean id="bCryptPasswordEncoder"
    class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider
        ref="authenticationProvider">
    </security:authentication-provider>
    <security:authentication-provider
        ref="restAuthenticationProvider">
    </security:authentication-provider>
</security:authentication-manager>

rest-security.xml:

<security:http create-session="stateless"
    entry-point-ref="digestEntryPoint" pattern="/provider/**"
    use-expressions="true">
    <security:intercept-url pattern="/provider/**"
        access="isAuthenticated()" />

    <security:http-basic />
    <security:custom-filter ref="digestFilter"
        after="BASIC_AUTH_FILTER" />
</security:http>

<bean id="digestFilter"
    class="org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
    <property name="userDetailsService" ref="webappDetailsServiceImpl" />
    <property name="authenticationEntryPoint" ref="digestEntryPoint" />
</bean>

<bean id="digestEntryPoint"
    class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
    <property name="realmName" value="Contacts Realm via Digest Authentication" />
    <property name="key" value="acegi" />
</bean>

Log:

22 Jun 2014 10:43:46 ERROR LoggingAspect - Unhandled exception caught: ...UserDetailsServiceImpl    loadUserByUsername
org.springframework.security.core.userdetails.UsernameNotFoundException: User with loginName: *** doesnt exist
at ...UserDetailsServiceImpl.loadUserByUsername(UserDetailsServiceImpl.java:30)
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:102)
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:132)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:168)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
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.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:409)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1044)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
22 Jun 2014 10:43:47  INFO LoggingAspect - Entering: ...WebappDetailsServiceImpl    Method name: loadUserByUsername Method arguments : [1]

回答1:

This stacktrace comes from your own code : UserDetailsServiceImpl line 30

According the Spring documentation :

AuthenticationProviders are usually tried in order until one provides a non-null response. A non-null response indicates the provider had authority to decide on the authentication request and no further providers are tried.

So what's going on ?

The first authenticationManager (bean : authenticationProvider) is invoked and throw a UserNameNotFoundException. That's OK and expected since authentication must fail with this one and must be successful with restAnthenticationProvider.

The stacktrace is printed by your LoggingAspect and the problem is there. LoggingAspect complains about "unexpected exception" BUT this one is not unexpected. The bean authenticationProvider is perfectly respecting the contract and throw the expected exception. So fix LoggingAspect so that it don't complains about this exception.

Note that : simply inverting the declaration order of the authentication provider will workaround the problem (at least for all rest request). The drawback of this quick workaround is that there are good chance that you will get an equivalent exception for all human authentication request since all of them will first fail against restAuthenticationProvider.