spring security 实现自定义认证和登录(4):使用token进行验证

前面我们实现了给客户端下发token,虽然客户端拿到了token,但我们还没处理客户端下一次携带token请求时如何验证,我们想要实现拿得到token之后,只需要验证token,不需要用户再携带用户名和密码了。



1. 禁用 UsernamePasswordAuthenticationFilter



在这里插入图片描述

由上图可以看出,当用户访问了相关路径之后,Spring内部会自己创建一个UsernamePasswordAuthenticationFilter给我们,现在我们不想通过username和password进行认证了,我们想通过token进行验证,那就需要把内部创建的UsernamePasswordAuthenticationFilter给禁用了。

添加formLogin().disable到过滤链中:


在这里插入图片描述


为什么添加了这行代码就禁用了那个东西了呢?这是因为如果不禁用formLogin,它就会创建一个UsernamePasswordAuthenticationFilter


2. 创建自己的认证过滤器


禁用了UsernamePasswordAuthenticationFilter之后,我们就需要创建自己的过滤器,并把它添加到过滤链中,我们的验证过程就是在我们自己编写的过滤器中进行

在这里插入图片描述

@Slf4j
@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter {

    @Resource
    RedisTemplate redisTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //如果是要登录,放行到登录页面
        if("/login2".equals(request.getRequestURI())){
            filterChain.doFilter(request, response);
            return;
        }
        //不是进行登录,说明前端会携带token
        String token = request.getHeader("Authorization");
        if(StrUtil.isEmpty(token))
            throw new RuntimeException("token 为空");
        //token类型:postman前端传来的token会在前面加一个类型和一个空格
        if(!StrUtil.startWith(token, "Bearer"))
            throw new RuntimeException("token 类型错误");
        
        //1. 验证token
        token = token.substring(7);
        JWTSigner jwtSigner = JWTSignerUtil.hs512("testttttt".getBytes(StandardCharsets.UTF_8));
        if (!JWTUtil.verify(token, jwtSigner)) {
            throw new RuntimeException("token 无效");
        }
        //2. 从token中取出用户名
        String username = (String) JWTUtil.parseToken(token).getPayload().getClaim("username");
        
        //3. 根据用户名从redis中取出password(登录时可以将UserDetails存储到redis)
        String password = (String) redisTemplate.opsForHash().get("user-details", username);
        if(password == null) throw new RemoteException("token 过期");
        log.info("--------> get password= {}", password);

        //4. new一个UserDetails,这个UserDetails会被存储到SecurityContextHolder中
        MyUserDetails userDetails = new MyUserDetails(new MyUser(1, username, password));
        
        //5. 将UserDetails存储到SecurityContextHolder中
        SecurityContextHolder.getContext().setAuthentication(UsernamePasswordAuthenticationToken.authenticated(userDetails
        , null, null));
        filterChain.doFilter(request, response);
    }
}

注意,代码中的第5步决定是否放行请求,只有将UserDetails存储到SecurityContextHolder中,过滤器才会放行。


将编写好的过滤器添加到过滤器链中:




在这里插入图片描述

.addFilterAt(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

3. 测试

可以看到,用户首次登录之后,后端成功返回一个token



在这里插入图片描述

接下来携带token请求另一个页面。


在这里插入图片描述


成功返回结果:

在这里插入图片描述


EX++++++++++++++

经过测验发现,放行和不放行完全取决于这行代码:


在这里插入图片描述

SecurityContextHolder.getContext().setAuthentication(UsernamePasswordAuthenticationToken.authenticated(userDetails, null, null));

我甚至可以直接写成

SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, null, null));

因为UsernamePasswordAuthenticationToken.authenticated(userDetails, null, null)这个函数在底层就是会直接new一个UsernamePasswordAuthenticationToken

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值