如何使用Spring Security在用户初始登录时强制更改密码 [英] How to Enforce Change Password on User's initial login using Spring Security

查看:690
本文介绍了如何使用Spring Security在用户初始登录时强制更改密码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Spring Security初次登录时实施强制密码更改的最优雅方法是什么?

What would be the most elegant way of implementing a force password change upon user's initial login using Spring Security?

我尝试实现自定义 AuthenticationSuccessHandler 此处所述,但正如 rodrigoap 。 / p>

I tried implementing a custom AuthenticationSuccessHandler as mentioned here, but as mentioned by rodrigoap, if a user manually inputs the URL at the address bar, the user will still be able to proceed to that page even if he didn't change his password.


我用过滤器ForceChangePasswordFilter做了这个。因为如果用户手动键入url,他们可以绕过更改密码表单。使用过滤器,请求总是被拦截。

I did this with a filter ForceChangePasswordFilter. Because if the user types the url by hand they can bypass the change password form. With the filter the request always get intercepted.

因此,我继续实施自定义过滤器。

As such, I proceeded with implementing a custom filter.

我的问题是,当我实现自定义过滤器并在其中发送重定向时,它再次通过过滤器导致无限重定向循环,如上所述here
我尝试通过在security-context.xml中声明两个http标签来实现所提到的解决方案,第一个标签具有 pattern 属性,但它仍然通过我的自定义过滤器:

My question is this, when I implement a custom filter and send a redirect inside it, it goes through the filter again causing an infinite redirect loop as mentioned here. I tried implementing the solution mentioned by declaring two http tags in my security-context.xml with the first tag having the pattern attribute as such but it still goes through my custom filter:

<http pattern="/resources" security="none"/>
<http use-expressions="true" once-per-request="false"
    auto-config="true">
  <intercept-url pattern="/soapServices/**" access="permitAll" requires-channel="https"/>
  ...
  <custom-filter position="LAST" ref="passwordChangeFilter" />
</http>
...
<beans:bean id="passwordChangeFilter"
  class="my.package.ForcePasswordChangeFilter"/>
<beans:bean id="customAuthenticationSuccessHandler"
  class="my.package.CustomAuthenticationSuccessHandler" >
</beans:bean>
<beans:bean id="customAuthenticationFailureHandler"
  class="my.package.CustomAuthenticationFailureHandler" >
  <beans:property name="defaultFailureUrl" value="/login"/>
</beans:bean>

我当前的实现是什么(有效):

What my current implementation is (which works) is:


  • 在我的自定义身份验证成功处理程序中,我设置了会话属性 isFirstLogin

  • In我的ForcePasswordChangeFilter,我检查会话 isFirstLogin 是否设置为


    • 如果是,那么我发送一个重定向到我的强制密码更改

    • 否则,我打电话给 chain.doFilter()

    • Inside my custom authentication success handler, I set a session attribute isFirstLogin
    • In my ForcePasswordChangeFilter, I check if the session isFirstLogin is set
      • If it is, then I send a redirect to my force password change
      • Else, I call chain.doFilter()

      我对此实现的问题是访问我的资源文件夹也会通过此过滤器导致我的页面失真(因为* .js和* .css未成功检索)。
      这就是我在我的安全应用程序context.xml中尝试使用两个< http> 标签的原因(这不起作用)。

      My problem with this implementation is that access to my resources folder also goes through this filter which causes my page to be distorted (because *.js and *.css are not successfully retrieved). This is the reason I tried having two <http> tags in my security app context.xml (which didn't work).

      因此,如果servletPath启动或包含/ resources,我最终必须手动过滤请求。
      我不希望它像这样 - 必须手动过滤请求路径 - 但是现在它就是我拥有的。

      As such, I ended up having to manually filter the request if the servletPath starts or contains "/resources". I didn't want it to be like this - having to manually filter the request path - but for now it's what I have.

      什么更优雅这样做的方式?

      What's the more elegant way of doing this?

      推荐答案

      我通过为用户提供状态值解决了这个问题,

      I solved this issue by providing a status value for the user,


      • status = -1;初次登录

      • status = 0; deactive account

      • status = 1;活动帐户

      security.xml 中的2个自定义身份验证控制器。首先检查用户名,传递和秒,以获取其他控件,如初始登录,密码到期策略。

      and 2 custom authentication controller in the security.xml. First for to check username, pass and second for the additional controls like initial login, password expiration policy.

      如果是首次登录,请提供正确的用户名和密码值,第一个控制器( user-service-ref =jdbcUserService)无法验证用户身份(因为用户的 status = -1 )比第二个控制器( ref =myAuthenticationController)捕获请求。在此控制器中,抛出 DisabledException

      In case of first login, providing correct values of username and password, first controller (user-service-ref="jdbcUserService") fails to authenticate user(because user's status=-1) than second controller(ref="myAuthenticationController") catches the request. In this controller DisabledException is thrown.

      最后,您可以将用户重定向到 AuthenticationFailureListener onAuthenticationFailure上的密码更改页面方法。

      Finally, you can redirect user to password-change page on AuthenticationFailureListener's onAuthenticationFailure method.

      security.xml的一部分

      <authentication-manager alias="authenticationManager">
          <authentication-provider user-service-ref="jdbcUserService">
              <password-encoder ref="passwordEncoder" />
          </authentication-provider>
          <authentication-provider ref="myAuthenticationController" />
      </authentication-manager>
      
      <beans:bean id="jdbcUserService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
          <beans:property name="rolePrefix" value="ROLE_" />
          <beans:property name="dataSource" ref="dataSource" />
          <beans:property name="usersByUsernameQuery" value="SELECT user_name as userName, PASSWORD as password, STATUS as status FROM  USER WHERE  user_name = ? AND STATUS=1" />
          <beans:property name="authoritiesByUsernameQuery" value="SELECT user_name as userName, ROLE as authority FROM USER WHERE user_name = ?" />
      </beans:bean>
      
      <beans:bean id="myAuthenticationController" class="com.test.myAuthenticationController">
          <beans:property name="adminUser" value="admin" />
          <beans:property name="adminPassword" value="admin" />
      </beans:bean>
      
      <!--Custom authentication success handler for logging/locking/redirecting-->
      
      <beans:bean id="authSuccessHandler" class="com.test.AuthenticationSuccessListener"/>
      
      <!--Custom authentication failure handler for logging/locking/redirecting-->
      
      <beans:bean id="authFailureHandler" class="com.test.AuthenticationFailureListener"/>
      







      @Service("myAuthenticationController")
      public class MyAuthenticationController extends AbstractUserDetailsAuthenticationProvider {
      
          private final Logger logger = Logger.getLogger(getClass());
      
          @Autowired
          private WfmUserValidator userValidator;
          private String username;
          private String password;
      
          @Required
          public void setAdminUser(String username) {
              this.username = username;
          }
      
          @Required
          public void setAdminPassword(String password) {
              this.password = password;
          }
      
          @Override
          protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
              return;
          }
      
          @Override
          protected UserDetails retrieveUser(String userName, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
              String password = (String) authentication.getCredentials();
              List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
              String userRole = "";
      
      
              if (status = -1) {
                  throw new DisabledException("It is first login. Password change is required!");
              } else if (password expired) {
                  throw new CredentialsExpiredException("Password is expired. Please change it!");
              }
      
              return new User(userName, password, true, // enabled
                      true, // account not expired
                      true, // credentials not expired
                      true, // account not locked
                      authorities);
          }
      }
      







      public class AuthenticationFailureListener implements AuthenticationFailureHandler {
      
          private static Logger logger = Logger.getLogger(AuthenticationFailureListener.class);
          private static final String BAD_CREDENTIALS_MESSAGE = "bad_credentials_message";
          private static final String CREDENTIALS_EXPIRED_MESSAGE = "credentials_expired_message";
          private static final String DISABLED_MESSAGE = "disabled_message";
          private static final String LOCKED_MESSAGE = "locked_message";
      
          @Override
          public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse res, AuthenticationException ex) throws IOException, ServletException {
              // TODO Auto-generated method stub
              String userName = req.getParameter("j_username");
              logger.info("[AuthenticationFailure]:" + " [Username]:" + userName + " [Error message]:" + ex.getMessage());
      
              if (ex instanceof BadCredentialsException) {
                  res.sendRedirect("../pages/login.jsf?message=" + MessageFactory.getMessageValue(BAD_CREDENTIALS_MESSAGE));
              } else if (ex instanceof CredentialsExpiredException) {
                  res.sendRedirect("../pages/changecredentials.jsf?message=" + MessageFactory.getMessageValue(CREDENTIALS_EXPIRED_MESSAGE));
              } else if (ex instanceof DisabledException) {
                  res.sendRedirect("../pages/changecredentials.jsf?message=" + MessageFactory.getMessageValue(DISABLED_MESSAGE));
              } else if (ex instanceof LockedException) {
                  res.sendRedirect("../pages/login.jsf?message=" + MessageFactory.getMessageValue(LOCKED_MESSAGE));
              }
          }
      }
      

      这篇关于如何使用Spring Security在用户初始登录时强制更改密码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆