Search code examples
javaspringspring-securitypasswordsj-security-check

Ask user to change their password after their password expires, but not during their session


Currently, the logic for my application is that, when the user's password expires after for example, 30 days, a user will be redirected to the Change Password screen even if they were in the middle of doing something. This is wrong. The user should only be prompted to change their password on the next login.

I have created a CheckAfterLoginFilter which extends OncePerRequestFilter where the logic resides. However, this is filtered on EVERY request so that user logs out in mid session. I am not sure how I can implement the desired logic in here if it's even possible.

My login form jsp uses j_security_check. My first thought was to move the logic from CheckAfterLoginFilter into the LoginController but j_security_check seems to redirect to it's own thing which I have no idea about or where to find.

Some help would be much appreciated!

Thanks


Solution

  • As you are using Spring Security, I'd assume you have already configured authenticationManager, and your UserEntity implements UserDetails.

    My suggestion would be to provide custom authentication failure handler and override isCredentialsNonExpired() in your UserEntity.

    Here is an example (with java based configurations).

    Custom authentication failure provider

    @Bean
    public AuthenticationFailureHandler customAuthenticationFailureHandler() {
        ExceptionMappingAuthenticationFailureHandler exceptionMappingAuthenticationFailureHandler =
                new ExceptionMappingAuthenticationFailureHandler();
        Map<Object, Object> map = new HashMap<>();
        map.put(
                "org.springframework.security.authentication.CredentialsExpiredException",
                "/resetPassword.html"
        );        
    
        exceptionMappingAuthenticationFailureHandler.setExceptionMappings(map);
    
        exceptionMappingAuthenticationFailureHandler.setRedirectStrategy(
                new RedirectStrategy() {
                    @Override
                    public void sendRedirect(
                            HttpServletRequest request, HttpServletResponse response, String url
                    ) throws IOException {
                        response.sendRedirect(request.getContextPath() + url);
                    }
                }
        );
    
        return exceptionMappingAuthenticationFailureHandler;
    }
    

    XML way

    <bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
        <property name="exceptionMappings">
            <props>
                <prop key="org.springframework.security.authentication.CredentialsExpiredException">/change_password_page</prop>
            </props>
        </property>
        <property name="defaultFailureUrl" value="/resetPassword"/>
    </bean>
    

    and in your security.xml

    <security:form-login ... authentication-failure-handler-ref="customAuthenticationFailureHandler">
    

    And finally in your UserEntity

        @Override
        public boolean isCredentialsNonExpired() {
            if (// check password is expired or not) {
               return false;
            }
            return true;
         }
    

    So when password expires, failure handler will redirect to your desired page.