2017-02-10 2 views
-1

Моя среда:Spring Security - авторизация пользователя иногда не удается, и пользователь перенаправляется на страницу входа

  • Java 6
  • Servlet 2.5
  • Weblogic 12,1
  • Spring MVC 4.3.4.RELEASE
  • Spring Security 4.2.0.RELEASE

Я внедрил CustomAuthenticationProvider для аутентификации пользователей с Oracle db: пользователи на самом деле являются db-пользователями, поэтому я пытаюсь подключиться к db для проверки пользователя/пароля, и если результат положительный, чем я загружаю полномочия.

Конфигурация работает хорошо, кроме как после ошибки входа. Если пользователь вводит неправильное имя пользователя или пароль, то приложение показывает сообщение об ошибке на той же странице, но дальнейшая правильная попытка не позволяет пользователю войти в систему. Вместо этого перенаправляется на страницу входа. Если пользователь повторяет попытку, проблема остается. Через минуту такая же попытка, без перезагрузки страницы, прошла успешно.

Как я уже говорил, проблема возникает только после ошибки входа. Если пользователь правильно вводит свои учетные данные при первой попытке или после выхода из системы, никаких проблем не возникает. Если он делает это после ошибки входа, то проблема возникает. Кроме того, ничего не происходит в моей среде разработки (локальный сервер приложений и различные браузеры) , но проблема возникает каждый раз при промежуточной среде (тот же сервер приложений, но централизованный и IE9-IE10-Edge). Я действительно не понимаю, в чем разница.

Я положил много журналов на моем CustomAuthenticationProvider и я могу видеть, чем в обоих случаях (положительные и отрицательные Логин) имя пользователя и пароль были успешно приняты и UsernamePasswordAuthenticationToken был создан. Затем пользователь должен быть перенаправлен на default-target-url, который является моим корнем приложения/(я установил always-use-default-target = 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> 

Каждое предложение ценится. Спасибо всем, dolfiz

EDIT: Я сделал журналы весной безопасности работает, и кажется, что по каким-то причинам сессия была очищена после аутентификации, так что вперед на страницу входа. Этих журналы:

[DEBUG] 2017-02-17 17:01:41.317 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication success. Updating SecurityContextHolder to contain: org.springframew[email protected]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.sprin[email protected]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.sprin[email protected]6faa3d44: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]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.sprin[email protected]6faa3d44: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]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.sp[email protected]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 

Почему это происходит только иногда и только на некоторых окружающих средах?

+0

Почему -1? По словам кого-то, мой вопрос не показывает каких-либо исследований, или, может быть, он неясен и/или не полезен: / – Dolfiz

ответ

0

Решения для чтения для другой, но аналогичной проблемы here и here, я догадался, что моя проблема параллелизма также связана с управлением сеансом.

По этой причине я попытался установить явную конфигурацию в моем securityConfig.xml для управления сеансами, ограничение на 1 число AUTH сеансов, разрешенных для каждого пользователя:

<session-management session-fixation-protection="newSession"> 
    <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" /> 
</session-management> 

И поставить калибровочную слушателя на моем web.xml:

<listener> 
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> 
</listener> 

Эти изменения полностью решают проблему.

Смежные вопросы