循序渐进学spring security 第十二篇 修改登录密码后如何让修改前的token失效?

回顾

前面我们介绍了《循序渐进学spring security 第十篇 如何用token登录?JWT闪亮登场》JWT 生成token的方式生成了登录凭证token,这样客户端只需要在请求接口上添加请求头token,服务端在过滤器中拦截并取出来token值,进行静默登录,但是有一个问题,不知道大家是否有留意到,如果用户修改了密码,并且重新登录过系统,而此时,如果还是用以前的token,还能继续访问系统,这对于系统安全性来说是不合理的

为什么会出现这样的问题?
因为我们在进行token验证的过程中,并没有验证密码是否已修改,因为token中我们也不会存储密码等敏感的数据,这样,我们拿到token解析出来也不会再进行密码的校验了,所以每次只需要携带有效的token,就能畅通无阻的访问各种有权限的资源了,这显然是一种安全隐患

如何解决这样的问题?
这就需要在token中添加多一个标识,这个标识,可以用密码通过MD5加密串为标识,这样,我们在服务端获取到token时,根据这个标识和当前登录用户的密码进行MD5加密比较,如果匹配,表示没有修改过密码,如果不匹配,则表示密码已经修改,需要重新登录才能访问

如何实现用户修改了密码,之前的token就失效?

用户修改密码后,需要重新登录

修改密码后需要重新登录,登录成功后,添加一个密码是否修改的标识,这个标识是一个将密码进行MD5加密的字符串,当然也可以其他的加密方式,只要能确保是通过密码生产出来的唯一标识即可,参考代码如下

public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Object principal = authentication.getPrincipal();
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();

        User user= (User) principal;
        //生成token
        Map<String, Object> claims=new HashMap<>();
        claims.put("username",user.getUsername());
        claims.put("authorities",user.getAuthorities());
        claims.put("enabled",user.isEnabled());
        claims.put("expiresIn",(System.currentTimeMillis()+expMillis));
        claims.put("passwordModifyTarget", MD5Util.MD5(MD5Util.MD5(user.getPassword())));

        String token = JWTUtils.getAccessToken(secret, claims);

        Map<String,Object>result=new HashMap<>();
        result.put("accessToken",token);
        out.write(JSON.toJSONString(result));
        out.flush();
        out.close();
    }

passwordModifyTarget 就是一个密码是否修改的标识字段

我这里将原始密码通过两次MD5算法作为标识,存到token里面,返回给前端,这样,后续前端请求后端接口时,只需要携带上这个token,后端就可以通过这个标识验证密码是否被修改过

验证密码是否被修改

在JwtAuthenticationTokenFilter 中的方法doFilterInternal获取到token后,进行校验

            UserDetails userDetails=null;
            if(SecurityContextHolder.getContext().getAuthentication() == null){
                //正常用户
                userDetails = userDetailsService.loadUserByUsername(username);
                if(userDetails!=null&&userDetails.isEnabled()){
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                    //设置用户登录状态
                    log.info("authenticated user {}, setting security context",username);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }else {
                userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            }
            String passwordModifyTarget = (String) parseJWT.get("passwordModifyTarget");
            //校验是否修改过密码
            if(passwordModifyTarget==null||!passwordModifyTarget.equals( MD5Util.MD5(MD5Util.MD5(userDetails.getPassword())))){
                //密码已经修改,重置数据,重新登录
                SecurityContextHolder.getContext().setAuthentication(null);
            }
  • 首先,判断SecurityContextHolder.getContext().getAuthentication()是否为空,如果为空,表示用户在系统中还没有登录,则根据token中的用户名去数据库查询该用户,并赋值给userDetails,再进行静默登录,如果不为空,则直接取出来用户信息,并赋值给userDetails对象
  • 接下来就是从token中取出来passwordModifyTarget标识,将userDetails中的密码进行两次MD5加密后和passwordModifyTarget比较,如果匹配则表示没有修改过密码,否则就是已经修改过了密码,当前token是无效的,将登录用户置空,让用户重新登录

这样,我们就实现了当用户修改了密码,并且重新登录后,让之前的token失效的功能了

我是基于上一篇文章《循序渐进学spring security 第十一篇 如何动态权限控制URL?如何动态给用户添加权限?》的项目进行修改的,如果对于我本次的讲解看不懂的,可以先去看看之前的文章

测试功能

启动项目,用户登录后(密码是12345678),得到token,用token去访问接口能正常访问
在这里插入图片描述

此时,如果修改密码(密码123456),因为我这里没有前端,修改密码暂时都是用测试类生成,然后直接改数据库

 @Test
    public void testPasswordEncoder() {
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put("bcrypt", new BCryptPasswordEncoder());
        encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
        encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
        encoders.put("noop", NoOpPasswordEncoder.getInstance());
        DelegatingPasswordEncoder encoder1 = new DelegatingPasswordEncoder("bcrypt", encoders);
        DelegatingPasswordEncoder encoder2 = new DelegatingPasswordEncoder("MD5", encoders);
        DelegatingPasswordEncoder encoder3 = new DelegatingPasswordEncoder("SHA-256", encoders);
        DelegatingPasswordEncoder encoder4 = new DelegatingPasswordEncoder("noop", encoders);
        String e1 = encoder1.encode("123456");
        String e2 = encoder2.encode("123456");
        String e3 = encoder3.encode("123456");
        String e4 = encoder4.encode("123456");
        System.out.println("e1 = " + e1);
        System.out.println("e2 = " + e2);
        System.out.println("e3 = " + e3);
        System.out.println("e4 = " + e4);
    }

得到结果

e1 = {bcrypt}$2a$10$O2g6F.DEa8GLz7RobTfbKev7sjLgyKbzT/izfOlRoDNT60XsBbbvC
e2 = {MD5}{YTtn8Xm5S/lb2OXuisc2T0R5w6N1A8QkAwCEIkwsU9s=}543ac55d279d5370df3cb2ea165ddd01
e3 = {SHA-256}{T7ntQNE0oCuaw+sIMdHNvlQG/QRpN9n3O49tTEnbbPc=}c62032509e67e0a2377aa60d1a2661acee255125d8c024a63ad5c84e40318377
e4 = {noop}123456

我这里取e1的值作为密码,赋值到数据库中,然后重新登录
在这里插入图片描述
登录成功后,我还用原来的token来访问,此时,是访问失败的,说明我们修改密码已经成功了

在这里插入图片描述

这样,我们就完成了用户修改密码后,让之前的token失效的功能了。我案例中是直接生成密码,然后修改数据库的,这一点大家在项目中,要结合页面进行交互完成操作,修改完密码,如果不需要用户重新登录的话,修改完成后调用SecurityContextHolder.getContext().setAuthentication()接口重新保存用户登录信息就可以

好了,关于修改密码让token失效就介绍到这里,源码可以在《循序渐进学spring security 第十一篇 如何动态权限控制URL?如何动态给用户添加权限?》中下载

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值