美文网首页
Spring Boot 如何通过 Security Contex

Spring Boot 如何通过 Security Contex

作者: 云大数据社区 | 来源:发表于2019-10-11 08:12 被阅读0次

    原文链接:http://leozzy.com/?p=119

    SecurityContextHolder 是用来保存 SecurityContext 的,通过 SecurityContextHolder.getContext() 静态方法可以获得当前 SecurityContext 对象。

    SecurityContext 持有代表当前用户相关信息的 Authentication 的引用, Authentication 通过 SecurityContext 对象的 getAuthentication() 方法获得。

    通过 Authentication.getPrincipal() 可以获取到代表当前用户的信息,这个对象通常是 UserDetails 的实例。

    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    

    获取到的 UserDetail 中包含用户名和密码等用户信息,但是该密码是经过 Spring 加密的并且不可逆(hash + salt)的。

    那么我们如何才能拿到明文的密码呢?

    每次登录时,表单中填写用户名密码,以这里作为切入点,找到登陆接口以及 Spring Security 对表单中的账号密码进行认证的地方,那就是自己定义的 AuthenticationProvider 的实现类,authenticate() 方法参数中有 Authentication 对象,这是 Spring 自己已经封装好的对象,其中包含账号密码信息。

    String username = authentication.getName();
    String password = (String) authentication.getCredentials();
    

    但这个 authenticate() 是由 SpringSecurity 来调用的,我们无法在其他方法中调用这个方法获取账号密码。那么就直接模仿该方法,通过 SecurityContextHolder.getContext().getAuthentication() 获得 Authentication 对象,而不是文章开头那样拿到 UserDetail 对象(包含的是加密后的密码),再通过 getCredentials 即可获得明文密码。

    就这么简单吗?通过调试发现除了在 AuthenticationProvider 实现类的 authenticate() 认证方法中能够通过这种方式获得明文密码,其他地方使用 Authentication 拿到的密码都是 null,原因如下:

    默认情况下,在认证成功后,ProviderManager 会清除返回的 Authentication 中的凭证信息,如密码。所以如果你在无状态的应用中将返回的 Authentication 信息缓存起来了,那么以后你再利用缓存的信息去认证将会失败,因为它已经不存在密码这样的凭证信息了。所以在使用缓存的时候你应该考虑到这个问题。一种解决办法是设置 ProviderManager 的 eraseCredentialsAfterAuthentication 属性为 false,或者想办法在缓存时将凭证信息一起缓存。

    那么关键是如何设置 eraseCredentialsAfterAuthentication 属性呢?

    在继承了 WebSecurityConfigurerAdapter 的类中,重写 configure(AuthenticationManagerBuilder auth) 方法,设置 ProviderManager 的属性即可。

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.eraseCredentials(false);
    }
    

    在页面退出登录时,会通过 clearAuthentication(true) 方法清空 SecurityContext Authentication 相关信息,以不同账号登录,保存的都是当时登录的 Authentication 信息。

    如果在页面修改密码,那么 Authentication 默认不会更新,需要自己手动更新 SecurityContext 中 Authentication 的信息。如果不退出登录,使用了 Authentication 的地方依然使用的旧密码。

    private void updateSecurityContext(String newPwd) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String username = authentication.getName();
        UserDetails user = kylinUserService.loadUserByUsername(username);
        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
        SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, newPwd, authorities));
    }
    

    相关文章

      网友评论

          本文标题:Spring Boot 如何通过 Security Contex

          本文链接:https://www.haomeiwen.com/subject/sggzpctx.html