Spring Security 自定义过滤器(更改密码)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3335559/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 04:05:37  来源:igfitidea点击:

Spring Security Custom Filter (Change Password)

springspring-security

提问by DrewEaster

I'm using Spring Security for securing HTTP requests to a website. The primary usage is for securing pages such that the user is redirected to the login page when trying to access those pages.

我正在使用 Spring Security 来保护对网站的 HTTP 请求。主要用途是保护页面,以便用户在尝试访问这些页面时被重定向到登录页面。

However, I have a further requirement. In my model, I can flag a user's password as being temporary such that, when they successfully login, they should be automatically forced to change their password. Once the password is changed, they should then be forwarded on to the page they were originally trying to access.

不过,我还有一个要求。在我的模型中,我可以将用户的密码标记为临时密码,这样,当他们成功登录时,应该会自动强制他们更改密码。更改密码后,他们应该被转发到他们最初尝试访问的页面。

Has anyone used Spring Security for this purpose? Do I need to create my own custom filter?

有没有人为此目的使用过 Spring Security?我需要创建自己的自定义过滤器吗?

Thanks,

谢谢,

Andrew

安德鲁

回答by axtavt

In Spring Security 3.0 you can implement a custom AuthenticationSuccessHandler.

在 Spring Security 3.0 中,您可以实现自定义AuthenticationSuccessHandler.

In this handler you can redirect a user with temporary password to the password change page instead of the originally requested page. After password is changed, you may redirect user to the originally requested page using SavedRequestAwareAuthenticationSuccessHandler, which is the default handler implementation.

在此处理程序中,您可以将具有临时密码的用户重定向到密码更改页面,而不是最初请求的页面。更改密码后,您可以使用 将用户重定向到最初请求的页面SavedRequestAwareAuthenticationSuccessHandler,这是默认的处理程序实现。

public class MyHandler implements AuthenticationSuccessHandler {
    private AuthenticationSuccessHandler target = new SavedRequestAwareAuthenticationSuccessHandler();

    public void onAuthenticationSuccess(HttpServletRequest request,
        HttpServletResponse response, Authentication auth) {
        if (hasTemporaryPassword(auth)) {
            response.sendRedirect("/changePassword");
        } else {
            target.onAuthenticationSuccess(request, response, auth);
        }
    }

    public void proceed(HttpServletRequest request, 
        HttpServletResponse response, Authentication auth) {
        target.onAuthenticationSuccess(request, response, auth);
    }
}

@Controller("/changePassword")
public class ChangePasswordController {

    @Autowired
    private MyHandler handler;

    @RequestMapping(method = POST)
    public void changePassword(HttpServletRequest request, 
        HttpServletResponse response,
        @RequestParam(name = "newPassword") String newPassword) {

        // handle password change
        ...

        // proceed to the secured page
        handler.proceed(request, response, auth);        
    }

    // form display method, etc
    ...
}

回答by jyore

A little late on this, but hopefully this can help others finding this link. If you use a custom UserDetailsService, you can set the User object's credentialsNonExpired to false, for example, to not allow access to any secure content until that field is set back to true.

有点晚了,但希望这可以帮助其他人找到这个链接。如果您使用自定义 UserDetailsS​​ervice,则可以将 User 对象的 credentialsNonExpired 设置为 false,例如,在该字段重新设置为 true 之前不允许访问任何安全内容。

Basically, when you have password expiration, you will set a field in your User model (passwordExpired maybe), and when your UserDetailsService pulls the user, your UserDetailsService will use that value to set credentialsNonExpired.

基本上,当您的密码到期时,您将在您的用户模型中设置一个字段(可能是 passwordExpired),并且当您的 UserDetailsS​​ervice 拉取用户时,您的 UserDetailsS​​ervice 将使用该值来设置凭据非过期。

Then, all you need to do is add some config to your applicationContext-security.xml to setup authentication exception mappings. This will allow you to catch the exception thrown with expired credentials and force the user to a reset password page. You can additionally catch locked and disabled accounts using a similar method. The config example is shown below:

然后,您需要做的就是向 applicationContext-security.xml 添加一些配置以设置身份验证异常映射。这将允许您捕获使用过期凭据抛出的异常并强制用户进入重置密码页面。您还可以使用类似的方法捕获锁定和禁用的帐户。配置示例如下所示:

applicationContext-security.xml

applicationContext-security.xml

<beans:bean id="exceptionTranslationFilter" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
    <beans:property name="exceptionMappings">
        <beans:props>           
            <beans:prop key="org.springframework.security.authentication.BadCredentialsException">/login_error</beans:prop>
            <beans:prop key="org.springframework.security.authentication.CredentialsExpiredException">/password_expired</beans:prop>
            <beans:prop key="org.springframework.security.authentication.LockedException">/locked</beans:prop>
            <beans:prop key="org.springframework.secuirty.authentication.DisabledException">/disabled</beans:prop>
        </beans:props>
        </beans:property>
</beans:bean>

<http use-expressions="true">
    <!-- ADD BLACKLIST/WHITELIST URL MAPPING -->
    <form-login login-page="/login" default-target-url="/" authentication-failure-handler-ref="exceptionTranslationFilter" />
</http>

Then just make sure you have your controllers setup to serve those links with the appropriate content.

然后只需确保您的控制器设置为使用适当的内容提供这些链接。

回答by rodrigoap

Yes, 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.

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

回答by ali gh

Very useful answer form jyore, it was exactly what I was looking for. In case you are using a custom class implementing UserDetailsService, you can do it as the following along with the above bean definition in your applicationContext.xml. One thing is that based on your CML header you might need to use <bean ....or <prop ...instead of <beans:bean ...or <beans:prop ...

jyore非常有用的答案,这正是我正在寻找的。如果您正在使用自定义类实现UserDetailsService,您可以按照以下方式以及applicationContext.xml. 一件事是,根据您的 CML 标头,您可能需要使用<bean ....<prop ...代替 <beans:bean ...<beans:prop ...

import ......

@Service("userService")
public class UserDetailsServiceImpl implements UserDetailsService {

private static Logger logger = LoggerFactory
        .getLogger(UserDetailsServiceImpl.class);

@Autowired
private UserDao userDao;

@Override
public UserDetails loadUserByUsername( String username )
    throws UsernameNotFoundException, DataAccessException , CredentialsExpiredException ,BadCredentialsException ,
    LockedException , DisabledException , UsernameNotFoundException
{
    User user = userDao.getUserByUsername( username );
    System.out.println("User Found");

    if( user == null ){
        // System.out.println("User Not Found");
        logger.error( "User Not Found");
        throw new UsernameNotFoundException( username + " is not found." );
    }

    if( user.isEnabled() == false ){
    // System.out.println("User not enabled");
    logger.error( "User not enabled");
       throw new DisabledException( "User not enabled" );
   }

    if( user.isLocked() == true ){
         //System.out.println("User is Locked");
        logger.error( "User is Locked");
          throw new LockedException( "User is Locked" );
      }
    if( user.isPasswordExpired() == true ){
        // System.out.println("Password Expired");
        logger.error( "Password Expired");
         throw new CredentialsExpiredException( "Password Expired" );
     }  

    return user;
  }
}