我有问题,有时得到AuthenticationCredentialsNotFoundException
。目前我认为这是一个线程问题。根据另一个问题(link)SecurityContext
通过不同线程之间的对象HttpSession
- 但由于某种原因,这不适合我。为什么SecurityContext的Authentication对象不能在线程间共享?
这是怎么了处理登录的那一刻:
public ShopAdminDTO login(String userEmail, String password) throws EmailAddressNotFoundException {
LOGGER.debug("Login request for " + userEmail);
// Create and initialize user details object for Spring Security authentication mechanism.
ShopAdminUserDetails userDetails = new ShopAdminUserDetails(userEmail, password, true, true, true, true, new ArrayList<GrantedAuthority>());
// Create authentication object for the Spring SecurityContext
Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, password, new ArrayList<GrantedAuthority>());
boolean requiresEmailActivation = this.shopAdminValidationTokenRepository.getRequiresEmailValidation(userEmail);
if(requiresEmailActivation == true) {
LOGGER.info("Login denied: Email is not validated yet.");
// IMPORTANT NOTE: We throw an EmailNotFoundException instead of a
// PleaseValidateYourEmailFirstException in order to NOT reveal
// that this email exists. So: Do not "FIX" this!
throw new EmailAddressNotFoundException();
}
LOGGER.debug("Email appears validated.");
try {
// Execute authentication chain to try user authentication
auth = this.adminAuthenticationProvider.authenticate(auth);
} catch(BadCredentialsException e) {
// FIXME Login: We could/should count and limit login attempts here?
LOGGER.info("Bad credentials found for: " + userEmail);
throw e;
}
LOGGER.info("User successfully authenticated [userEmail="+userEmail+"]");
// Set the authentication to the SecurityContext, the user is now logged in
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(auth);
// Finally load the user data
ShopAdminDTO shopAdminDto = this.shopAdminRepository.findByUserEmail(userEmail);
return shopAdminDto;
}
这是的applicationContext-security.xml文件文件
<!-- //////////////////////////////////////////////////////////////////////////////// -->
<!-- // BEGIN Spring Security -->
<sec:http pattern="/**" auto-config="true" use-expressions="true"/>
<bean id="httpSessionSecurityContextRepository" class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
<property name='allowSessionCreation' value='false' />
</bean>
<bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<constructor-arg ref="httpSessionSecurityContextRepository" />
</bean>
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<sec:filter-chain pattern="/**" filters="securityContextPersistenceFilter" />
</list>
</constructor-arg>
</bean>
<bean id="authenticationListener" class="com.mz.server.web.auth.CustomAuthenticationListener"/>
<bean id="authenticationProvider" class="com.mz.server.web.auth.CustomAuthenticationProvider"/>
<bean id="userDetailsService" class="com.mz.server.web.service.CustomUserDetailsService"/>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="authenticationProvider"/>
</sec:authentication-manager>
<bean id="permissionEvaluator"
class="com.mz.server.web.auth.permission.CustomPermissionEvaluator">
<constructor-arg index="0">
<map key-type="java.lang.String"
value-type="com.mz.server.web.auth.permission.Permission">
<entry key="isTest" value-ref="testPermission"/>
</map>
</constructor-arg>
</bean>
<bean id="testPermission" class="com.mz.server.web.auth.permission.TestPermission">
</bean>
<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator"/>
</bean>
<sec:global-method-security
authentication-manager-ref="authenticationManager"
pre-post-annotations="enabled">
<sec:expression-handler ref="expressionHandler"/>
</sec:global-method-security>
<!-- // END Spring Security -->
<!-- //////////////////////////////////////////////////////////////////////////////// -->
什么失败是AbstractSecurityInterceptor#beforeInvocation
这部分功能:
if (debug) {
logger.debug("Secure object: " + object + "; Attributes: " + attributes);
}
if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage(
"AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"),
object, attributes);
}
Authentication authenticated = authenticateIfRequired();
Wh因为SecurityContextHolder.getContext().getAuthentication()
是null
,所以它叫credentialsNotFound
。
服务器开机后出现故障的堆栈跟踪从第一登录比较:
[http-bio-8080-exec-4] DEBUG com.mz.server.servlet.LoginServletImpl - Login request by userId: [email protected]
[http-bio-8080-exec-4] DEBUG com.mz.server.service.LoginService - Login request for [email protected]
[http-bio-8080-exec-4] DEBUG com.mz.server.service.LoginService - Email appears validated.. authenticating..
[http-bio-8080-exec-4] INFO com.mz.server.spring.auth.AdminAuthenticationProvider - authenticate(), User email: [email protected]
[http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): [email protected]
[http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - User found.
[http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Loading password salt for [email protected]
[http-bio-8080-exec-4] INFO com.mz.server.repository.jooq.shop.ShopAdminRepository - Checking password for [email protected]
[http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Password valid.
[http-bio-8080-exec-4] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - getPrincipal()
[http-bio-8080-exec-4] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - Setting user [email protected]: Username: [email protected]; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities to 'authenticated'.
[http-bio-8080-exec-4] INFO com.mz.server.service.LoginService - User successfully authenticated [[email protected]]
[http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): [email protected]
[http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - User found.
[http-bio-8080-exec-6] DEBUG com.mz.server.servlet.shop.ShopServletImpl - Requested available shops.
[http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl -
[http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl -
[http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl - SPRING_SECURITY_CONTEXT: [email protected]9bee56: Authentication: [email protected]
[http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl -
[http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl -
[http-bio-8080-exec-6] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List com.mz.server.service.ShopService.getAvailableShops(); target is of class [com.mz.server.service.ShopService]; Attributes: [[authorize: 'isAuthenticated()', filter: 'null', filterTarget: 'null']]
[http-bio-8080-exec-6] DEBUG com.mz.server.spring.auth.CustomHttpSessionListener - AuthenticationCredentialsNotFoundEvent
Jun 09, 2016 8:06:42 PM org.apache.catalina.core.ApplicationContext log
SEVERE: Exception while dispatching incoming RPC call
com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract java.util.List com.mz.shared.web.service.shop.ShopServlet.getAvailableShops()' threw an unexpected exception: org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at com.google.gwt.user.server.rpc.RPC.encodeResponseForFailure(RPC.java:416)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:605)
....
到工作堆栈跟踪:
[http-bio-8080-exec-7] DEBUG com.mz.server.servlet.LoginServletImpl - Login request by userId: [email protected]
[http-bio-8080-exec-7] DEBUG com.mz.server.service.LoginService - Login request for [email protected]
[http-bio-8080-exec-7] DEBUG com.mz.server.service.LoginService - Email appears validated.. authenticating..
[http-bio-8080-exec-7] INFO com.mz.server.spring.auth.AdminAuthenticationProvider - authenticate(), User email: [email protected]
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): [email protected]
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - User found.
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Loading password salt for [email protected]
[http-bio-8080-exec-7] INFO com.mz.server.repository.jooq.shop.ShopAdminRepository - Checking password for [email protected]
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Password valid.
[http-bio-8080-exec-7] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - getPrincipal()
[http-bio-8080-exec-7] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - Setting user [email protected]: Username: [email protected]; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities to 'authenticated'.
[http-bio-8080-exec-7] INFO com.mz.server.service.LoginService - User successfully authenticated [[email protected]]
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): [email protected]
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - User found.
[http-bio-8080-exec-7] DEBUG com.mz.server.servlet.shop.ShopServletImpl - Requested available shops.
[http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl -
[http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl -
[http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl - SPRING_SECURITY_CONTEXT: [email protected]a22883: Authentication: [email protected]
[http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl -
[http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl -
[http-bio-8080-exec-7] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List com.mz.server.service.ShopService.getAvailableShops(); target is of class [com.mz.server.service.ShopService]; Attributes: [[authorize: 'isAuthenticated()', filter: 'null', filterTarget: 'null']]
[http-bio-8080-exec-7] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - isAuthenticate(): true
[http-bio-8080-exec-7] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Previously Authenticated: [email protected]
[http-bio-8080-exec-7] DEBUG org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframewor[email protected]653fccd, returned: 1
[http-bio-8080-exec-7] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Authorization successful
[http-bio-8080-exec-7] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - RunAsManager did not change Authentication object
[http-bio-8080-exec-7] DEBUG com.mz.server.service.ShopService - Getting available shops for ..
[http-bio-8080-exec-7] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - getPrincipal()
[http-bio-8080-exec-7] DEBUG com.mz.server.service.ShopService - [email protected]
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Fetching shops for shop_admin_id 1
我们可以看到差异是第一个stacktrace是由两个线程[http-bio-8080-exec-4]
和[http-bio-8080-exec-6]
。我经常看到这种行为,即线程名称发生更改,然后出现此异常。因此,这似乎是一个多线程的问题
这是整个web.xml中:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>mz | life</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/**</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<listener>
<listener-class>com.mz.server.BootstrappingServerConfig</listener-class>
</listener>
<!-- -->
<servlet>
<servlet-name>application</servlet-name>
<servlet-class>com.mz.server.servlet.app.ApplicationDataServletImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>application</servlet-name>
<url-pattern>/app/application</url-pattern>
</servlet-mapping>
<!-- -->
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.mz.server.servlet.LoginServletImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/app/login</url-pattern>
</servlet-mapping>
<!-- -->
<servlet>
<servlet-name>shop</servlet-name>
<servlet-class>com.mz.server.servlet.shop.ShopServletImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>shop</servlet-name>
<url-pattern>/app/shop</url-pattern>
</servlet-mapping>
<!-- -->
<servlet>
<servlet-name>shopadmin</servlet-name>
<servlet-class>com.mz.server.servlet.shop.ShopAdminServletImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>shopadmin</servlet-name>
<url-pattern>/app/shopadmin</url-pattern>
</servlet-mapping>
<!--
XSRF-Token Servlet
-->
<servlet>
<servlet-name>xsrf</servlet-name>
<servlet-class>com.google.gwt.user.server.rpc.XsrfTokenServiceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>xsrf</servlet-name>
<url-pattern>/app/xsrf</url-pattern>
</servlet-mapping>
<!--
This is the name of the session cookie set by the Servlet container (e.g. Tomcat or Jetty)
-->
<context-param>
<param-name>gwt.xsrf.session_cookie_name</param-name>
<param-value>JSESSIONID</param-value>
</context-param>
<!-- -->
<servlet>
<servlet-name>mobile-restapi</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mobile-restapi</servlet-name>
<url-pattern>/app/restapi/*</url-pattern>
</servlet-mapping>
<!-- -->
<servlet>
<servlet-name>web-restapi</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/context/applicationContext-restapi.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>web-restapi</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/context/root-context.xml
</param-value>
</context-param>
</web-app>
你知道吗,不同的请求,试图在第一时间访问什么资源?你如何提供静态内容?有可能是其中一个线程试图从登录页面访问一些受保护的资源(image/css)? –
@StefanHaberl那么,您看到日志的请求来自点击登录按钮。这不仅仅是查看数据库并检查给定用户名的密码是否正确。请参阅'用户成功验证[[email protected]]'条目,它告诉我登录实际上正在工作。所以不,在这个行动中没有额外的资源。 – displayname