具有多个身份验证提供程序的 Spring 安全性 - UsernameNotFoundException
Posted
技术标签:
【中文标题】具有多个身份验证提供程序的 Spring 安全性 - UsernameNotFoundException【英文标题】:Spring security with multiple authentication providers - UsernameNotFoundException 【发布时间】:2014-08-12 12:10:25 【问题描述】:我们有 2 个身份验证提供程序的 spring 安全配置:一个用于人类用户,第二个用于其他 webapps(通过 REST)。见我之前的问题:Spring security for both web services and users
现在,问题是,当一个webapp发送用户名+pass时,spring security首先尝试用户身份验证提供者,失败,然后尝试其余身份验证提供者。
这会导致日志中出现org.springframework.security.core.userdetails.UsernameNotFoundException
,即使其余身份验证最终成功。有没有办法防止这种情况发生?
配置是:
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>
日志:
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】:此堆栈跟踪来自您自己的代码:UserDetailsServiceImpl 第 30 行
根据 Spring 文档:
AuthenticationProvider 通常会按顺序尝试,直到提供非空响应。非空响应表示提供者有权决定身份验证请求,并且不会尝试其他提供者。
那么发生了什么?
第一个 authenticationManager (bean: authenticationProvider) 被调用并抛出一个 UserNameNotFoundException。这没关系,也是意料之中的,因为此验证必须失败,并且restAnthenticationProvider
必须成功。
堆栈跟踪由您的 LoggingAspect 打印,问题就在那里。 LoggingAspect 抱怨“意外异常”,但这并不意外。 bean authenticationProvider 完全遵守合同并抛出预期的异常。所以修复 LoggingAspect 使它不会抱怨这个异常。
请注意:只需颠倒身份验证提供程序的声明顺序即可解决该问题(至少对于所有休息请求)。这种快速解决方法的缺点是,您很有可能会为所有人工身份验证请求获得等效异常,因为所有人工身份验证请求都会首先针对 restAuthenticationProvider 失败。
【讨论】:
谢谢,您能否举例说明如何修复日志记录方面,以便它只会忽略这种情况下的异常(其余身份验证),而不是在用户凭据实际上错误的情况下? euh...不,如果没有 LoggingAspect 的当前代码,我将无法做到这一点。 好的,那么您知道如何从 aspectj 中排除特定异常吗? 这取决于你如何配置你的方面。也许问一个新问题,因为这是一个非常不同的话题。 再次感谢,我做到了:***.com/questions/22633187/…以上是关于具有多个身份验证提供程序的 Spring 安全性 - UsernameNotFoundException的主要内容,如果未能解决你的问题,请参考以下文章
特定 url 的多个身份验证提供程序 - Spring Boot Security
具有 Spring Security 和 Java Config 的自定义身份验证提供程序