我的环境:
我已经实现了CustomAuthenticationProvider以便根据Oracle数据库验证用户身份:用户实际上是数据库用户,因此我尝试连接到数据库以检查用户/密码,并且结果是否比我加载权限大。
除登录错误后外,配置工作正常。如果用户键入错误的用户名或密码,则应用程序将在同一页面上显示错误消息,但是进一步的正确尝试不会使用户登录。而是重定向到登录页面。如果用户再次尝试,则问题仍然存在。一分钟后,无需重新加载页面即可进行相同的尝试。
就像我说的那样,仅在登录错误之后才出现此问题。如果用户在第一次尝试或注销后正确键入了自己的凭据,则不会发生任何问题。如果他在登录错误后执行此操作,则问题将出现。另外,在我的开发环境(本地应用服务器和各种浏览器)上没有发生任何错误,但是问题在每次升级环境(相同的应用服务器但集中式和IE9-IE10-Edge)上每次都会出现。我真的不明白是什么与众不同。
我在CustomAuthenticationProvider上放了很多日志,可以看到在两种情况下(正向和负向登录)用户名和密码均已成功接受,并且创建了UsernamePasswordAuthenticationToken。然后,应该将用户重定向到默认目标URL,即我的应用程序根目录/(我将always-use-default-target = true设置为true)。由于我不明白的原因,当问题发生时,重定向失败,因为Spring Security认为用户尚未获得访问安全路径的权限,因此会再次将其重定向到登录页面。
我已经用两种形式的登录表单提交检查了请求,除了传递的JSESSIONID之外,它们实际上是相同的。但是登录成功,我可以从响应标头中看到已设置JSESSIONID cookie,在否定的情况下,则没有“ set cookie”。
尽管提交的用户名和密码相同,但为什么会有这种行为上的差异呢?有什么可以改变的?我的猜测是错误的登录尝试会留下一些肮脏的东西。有一些影响下一次尝试。会是什么呢?为什么只在我的本地环境中出现此问题?我想念什么?
这是CustomAuthenticationProvider的实现:
@Component
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private Logger log = LogManager.getLogger(CustomAuthenticationProvider.class);
@Autowired
private UserService userService;
@Autowired
private SecurityService securityService;
@Autowired
private Messages messages;
@Value("${login.test.mode}")
private String testMode;
@Value("${login.test.mode.userid}")
private String testModeUserid;
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
log.debug("##### SECURITY ##### Test mode status: " + testMode);
// test mode uses its own configured user, ignoring login credentials, if username is empty
if (Constants.FLAG_YES.equals(testMode) && StringUtils.isEmpty(username)) {
username = testModeUserid;
}
GddbUserDetails gddbUserDetails = userService.findGddbUserDetailsByUsername(username);
UserRole userRole = userService.findUserRolesByUsername(username);
if (gddbUserDetails == null) {
log.debug("##### SECURITY ##### Utente non trovato in anagrafica GDDB: " + username);
throw new BadCredentialsException(messages.get("user.not.found.gddb"));
} else {
log.debug("##### SECURITY ##### OK Utente trovato in anagrafica GDDB: " + username);
}
// perform checks only if test mode is disabled
if (!Constants.FLAG_YES.equals(testMode)) {
// GDDB state check
if (!Constants.USER_STATO_ACTIVE.equals(gddbUserDetails.getStato())) {
log.debug("##### SECURITY ##### Utente presente in anagrafica GDDB ma disabilitato: " + username);
throw new BadCredentialsException(messages.get("user.not.enabled.gddb"));
} else {
log.debug("##### SECURITY ##### Utente presente in anagrafica GDDB e abilitato: " + username);
}
// dbetichette user existence check
if (userRole == null) {
log.debug("##### SECURITY ##### Utente non presente in anagrafica DBEtichette: " + username);
throw new BadCredentialsException(messages.get("user.not.enabled.locally"));
} else {
log.debug("##### SECURITY ##### Utente presente in anagrafica DBEtichette: " + username);
}
// dbetichette user activation check
if (!Constants.FLAG_YES.equals(userRole.getActive())) {
log.debug("##### SECURITY ##### Utente disabilitato in anagrafica DBEtichette: " + username);
throw new BadCredentialsException(messages.get("user.not.enabled.locally"));
} else {
log.debug("##### SECURITY ##### Utente abilitato in anagrafica DBEtichette: " + username);
}
// oracle user password check
String usernamePasswordCheckResult = securityService.checkUserPassword(username, password);
log.debug("##### SECURITY ##### usernamePasswordCheckResult: " + usernamePasswordCheckResult);
if (Constants.SECURITY_ACCOUNT_LOCKED.equals(usernamePasswordCheckResult)) {
log.debug("##### SECURITY ##### Utente presente su DB ma bloccato: " + username);
throw new BadCredentialsException(messages.get("user.blocked"));
} else if (Constants.SECURITY_PASSWORD_EXPIRED.equals(usernamePasswordCheckResult)) {
log.debug("##### SECURITY ##### Password dell'utente scaduta: " + username);
throw new BadCredentialsException(messages.get("user.password.expired"));
} else if (Constants.SECURITY_INVALID_USERNAME_PASSWORD.equals(usernamePasswordCheckResult)) {
log.debug("##### SECURITY ##### Tentativo di accesso fallito per errata password: " + username);
throw new BadCredentialsException(messages.get("user.password.wrong"));
} else if (!Constants.SECURITY_VALID_USERNAME_PASSWORD.equals(usernamePasswordCheckResult)) {
log.debug("##### SECURITY ##### Tentativo di accesso fallito per motivo sconosciuto: " + username
+ " ( usernamePasswordCheckResult = " + usernamePasswordCheckResult + " )");
throw new BadCredentialsException(messages.get("user.login.error.other"));
} else {
log.debug("##### SECURITY ##### Tentativo di accesso eseguito con successo: " + usernamePasswordCheckResult + " - " + username);
}
}
CustomUser user = userService.createCustomUser(gddbUserDetails, userRole);
log.debug("##### SECURITY ##### Creazione custom user: " + user);
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
UsernamePasswordAuthenticationToken userToken = new UsernamePasswordAuthenticationToken(user, password, authorities);
log.debug("##### SECURITY ##### Creazione userToken: " + userToken);
return userToken;
}
@Override
protected UserDetails retrieveUser(String s, UsernamePasswordAuthenticationToken token) throws AuthenticationException {
UserDetails user = (UserDetails) token.getPrincipal();
log.debug("##### SECURITY ##### retrieveUser: " + user);
return user;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken token) throws AuthenticationException {
log.debug("##### SECURITY ##### additionalAuthenticationChecks - userDetails " + userDetails);
log.debug("##### SECURITY ##### additionalAuthenticationChecks - token " + token);
}
}
这是我的Spring Security配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http auto-config="true">
<intercept-url pattern="/assets/**" access="permitAll()"/>
<intercept-url pattern="/pages/**" access="permitAll()"/>
<intercept-url pattern="/login" access="permitAll()"/>
<intercept-url pattern="/loginApp" access="permitAll()"/>
<intercept-url pattern="/loginFailed" access="permitAll()"/>
<intercept-url pattern="/logout" access="permitAll()"/>
<intercept-url pattern="/logoutSuccess" access="permitAll()"/>
<intercept-url pattern="/changepwd" access="permitAll()"/>
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/relabel/**" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_WAREHOUSE_OP') or hasRole('ROLE_QA')"/>
<intercept-url pattern="/**" access="hasRole('ROLE_DATA_ENTRY') or hasRole('ROLE_APPROVER') or hasRole('VIEWER') or hasRole('ROLE_ADMIN') or hasRole('ROLE_WAREHOUSE_OP') or hasRole('ROLE_QA')"/>
<form-login login-page="/login"
default-target-url="/"
authentication-failure-url="/loginFailed"
login-processing-url="/loginApp"
username-parameter="username"
password-parameter="password"
always-use-default-target="true"
/>
<logout logout-success-url="/logoutSuccess" logout-url="/logout"/>
<access-denied-handler error-page="/403"/>
<csrf disabled="true" />
</http>
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider"/>
</authentication-manager>
每个建议都表示赞赏。谢谢大家,多尔菲兹
编辑:我使spring安全日志工作,并且似乎由于某种原因,身份验证后会话已被清除,因此转发到登录页面。这些是日志:
[DEBUG] 2017-02-17 17:01:41.317 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@e2fe4b0e: Principal: CustomUser{username='MAROTAN1', password='null', email='[email protected]', firstName='Antonio', lastName='Marotta', graceTime='null', authorities=[Role{name='ROLE_ADMIN'}], accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true}; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 10.166.243.87; SessionId: QYWPYnpbth0y139v2gz7r6hCm0cHpsfmxq8DFqsvv3XM1kT6YcP2!2062762872!1487347291632; Granted Authorities: Role{name='ROLE_ADMIN'}
[DEBUG] 2017-02-17 17:01:41.317 org.springframework.security.web.DefaultRedirectStrategy - Redirecting to '/dbetichette/'
[DEBUG] 2017-02-17 17:01:41.317 org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession is now null, but was not null at start of request; session was invalidated, so do not create a new session
[DEBUG] 2017-02-17 17:01:41.317 org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
[DEBUG] 2017-02-17 17:01:41.321 org.springframework.security.web.FilterChainProxy - / at position 1 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
[DEBUG] 2017-02-17 17:01:41.321 org.springframework.security.web.context.HttpSessionSecurityContextRepository - No HttpSession currently exists
[DEBUG] 2017-02-17 17:01:41.322 org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
[DEBUG] 2017-02-17 17:01:41.322 org.springframework.security.web.FilterChainProxy - / at position 2 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
[DEBUG] 2017-02-17 17:01:41.322 org.springframework.security.web.FilterChainProxy - / at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
[DEBUG] 2017-02-17 17:01:41.323 org.springframework.security.web.FilterChainProxy - / at position 4 of 12 in additional filter chain; firing Filter: 'LogoutFilter'
[DEBUG] 2017-02-17 17:01:41.323 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/logout'
[DEBUG] 2017-02-17 17:01:41.323 org.springframework.security.web.FilterChainProxy - / at position 5 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
[DEBUG] 2017-02-17 17:01:41.323 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /' doesn't match 'POST /loginApp
[DEBUG] 2017-02-17 17:01:41.323 org.springframework.security.web.FilterChainProxy - / at position 6 of 12 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
[DEBUG] 2017-02-17 17:01:41.324 org.springframework.security.web.FilterChainProxy - / at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
[DEBUG] 2017-02-17 17:01:41.324 org.springframework.security.web.FilterChainProxy - / at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
[DEBUG] 2017-02-17 17:01:41.324 org.springframework.security.web.FilterChainProxy - / at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
[DEBUG] 2017-02-17 17:01:41.324 org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@6faa3d44: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff4c9c: RemoteIpAddress: 10.166.243.87; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
[DEBUG] 2017-02-17 17:01:41.325 org.springframework.security.web.FilterChainProxy - / at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
[DEBUG] 2017-02-17 17:01:41.325 org.springframework.security.web.session.SessionManagementFilter - Requested session ID QYWPYnpbth0y139v2gz7r6hCm0cHpsfmxq8DFqsvv3XM1kT6YcP2!2062762872 is invalid.
[DEBUG] 2017-02-17 17:01:41.325 org.springframework.security.web.FilterChainProxy - / at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
[DEBUG] 2017-02-17 17:01:41.325 org.springframework.security.web.FilterChainProxy - / at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
[DEBUG] 2017-02-17 17:01:41.325 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/assets/**'
[DEBUG] 2017-02-17 17:01:41.325 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/pages/**'
[DEBUG] 2017-02-17 17:01:41.326 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/login'
[DEBUG] 2017-02-17 17:01:41.326 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/loginApp'
[DEBUG] 2017-02-17 17:01:41.326 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/loginFailed'
[DEBUG] 2017-02-17 17:01:41.326 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/logout'
[DEBUG] 2017-02-17 17:01:41.326 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/logoutSuccess'
[DEBUG] 2017-02-17 17:01:41.326 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/changepwd'
[DEBUG] 2017-02-17 17:01:41.327 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/admin/**'
[DEBUG] 2017-02-17 17:01:41.327 org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/relabel/**'
[DEBUG] 2017-02-17 17:01:41.327 org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /; Attributes: [hasRole('ROLE_DATA_ENTRY') or hasRole('ROLE_APPROVER') or hasRole('VIEWER') or hasRole('ROLE_ADMIN') or hasRole('ROLE_WAREHOUSE_OP') or hasRole('ROLE_QA')]
[DEBUG] 2017-02-17 17:01:41.327 org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@6faa3d44: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff4c9c: RemoteIpAddress: 10.166.243.87; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
[DEBUG] 2017-02-17 17:01:41.328 org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@14b4feab, returned: -1
[DEBUG] 2017-02-17 17:01:41.329 org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
我想重要的一行是:
[DEBUG] 2017-02-17 17:01:41.317 org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession is now null, but was not null at start of request; session was invalidated, so do not create a new session
为什么仅在某些环境下才发生这种情况?
在这里和这里阅读针对不同但相似问题的解决方案时,我猜想我的也是一个并发问题,与会话管理有关。
因此,我尝试在securityConfig.xml
会话管理中设置显式配置,将每个用户允许的身份验证会话数限制为1:
<session-management session-fixation-protection="newSession">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
并将所需的侦听器放在my上web.xml
:
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
这些变化完全解决了问题。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句