Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 1481|回复: 0

[默认分类] Springboot token令牌验证解决方案 在SpringBoot实现基于Token的用户身份验证

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2020-8-12 11:50:31 | 显示全部楼层 |阅读模式
    1.首先了解一下Token


    1、token也称作令牌,由uid+time+sign[+固定参数]组成:

    uid: 用户唯一身份标识
    time: 当前时间的时间戳
    sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接
    固定参数(可选): 将一些常用的固定参数加入到 token 中是为了避免重复查数据库

      
    2.token 验证的机制(流程)

    用户登录校验,校验成功后就返回Token给客户端。
    客户端收到数据后保存在客户端
    客户端每次访问API是携带Token到服务器端。
    服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码

    3.使用Springboot搭建基于token验证
      
    3.1 引入 POM 依赖

    1.         <dependency>
    2.               <groupId>com.auth0</groupId>
    3.               <artifactId>java-jwt</artifactId>
    4.               <version>3.4.0</version>
    5.         </dependency>
    复制代码


    3.2  新建一个拦截器配置 用于拦截前端请求 实现   WebMvcConfigurer  

    1. 1 /***
    2. 2  * 新建Token拦截器
    3. 3 * @Title: InterceptorConfig.java
    4. 4 * @author MRC
    5. 5 * @date 2019年5月27日 下午5:33:28
    6. 6 * @version V1.0
    7. 7  */
    8. 8 @Configuration
    9. 9 public class InterceptorConfig implements WebMvcConfigurer {
    10. 10     @Override
    11. 11     public void addInterceptors(InterceptorRegistry registry) {
    12. 12         registry.addInterceptor(authenticationInterceptor())
    13. 13                 .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
    14. 14     }
    15. 15     @Bean
    16. 16     public AuthenticationInterceptor authenticationInterceptor() {
    17. 17         return new AuthenticationInterceptor();// 自己写的拦截器
    18. 18     }
    19.     
    20.     //省略其他重写方法
    21. 19
    22. 20 }
    复制代码


    3.3 新建一个 AuthenticationInterceptor  实现HandlerInterceptor接口  实现拦截还是放通的逻辑

    1. 1 public class AuthenticationInterceptor implements HandlerInterceptor {
    2. 2     @Autowired
    3. 3     UserService userService;
    4. 4     @Override
    5. 5     public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
    6. 6         String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
    7. 7         // 如果不是映射到方法直接通过
    8. 8         if(!(object instanceof HandlerMethod)){
    9. 9             return true;
    10. 10         }
    11. 11         HandlerMethod handlerMethod=(HandlerMethod)object;
    12. 12         Method method=handlerMethod.getMethod();
    13. 13         //检查是否有passtoken注释,有则跳过认证
    14. 14         if (method.isAnnotationPresent(PassToken.class)) {
    15. 15             PassToken passToken = method.getAnnotation(PassToken.class);
    16. 16             if (passToken.required()) {
    17. 17                 return true;
    18. 18             }
    19. 19         }
    20. 20         //检查有没有需要用户权限的注解
    21. 21         if (method.isAnnotationPresent(UserLoginToken.class)) {
    22. 22             UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
    23. 23             if (userLoginToken.required()) {
    24. 24                 // 执行认证
    25. 25                 if (token == null) {
    26. 26                     throw new RuntimeException("无token,请重新登录");
    27. 27                 }
    28. 28                 // 获取 token 中的 user id
    29. 29                 String userId;
    30. 30                 try {
    31. 31                     userId = JWT.decode(token).getAudience().get(0);
    32. 32                 } catch (JWTDecodeException j) {
    33. 33                     throw new RuntimeException("401");
    34. 34                 }
    35. 35                 User user = userService.findUserById(userId);
    36. 36                 if (user == null) {
    37. 37                     throw new RuntimeException("用户不存在,请重新登录");
    38. 38                 }
    39. 39                 // 验证 token
    40. 40                 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
    41. 41                 try {
    42. 42                     jwtVerifier.verify(token);
    43. 43                 } catch (JWTVerificationException e) {
    44. 44                     throw new RuntimeException("401");
    45. 45                 }
    46. 46                 return true;
    47. 47             }
    48. 48         }
    49. 49         return true;
    50. 50     }
    51. 51
    52. 52     @Override
    53. 53     public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    54. 54
    55. 55     }
    56. 56     @Override
    57. 57     public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    58. 58
    59. 59     }
    60. 60 }
    复制代码


    3.4 新建两个注解 用于标识请求是否需要进行Token 验证

    1. /***
    2. * 用来跳过验证的 PassToken
    3. * @author MRC
    4. * @date 2019年4月4日 下午7:01:25
    5. */
    6. @Target({ElementType.METHOD, ElementType.TYPE})
    7. @Retention(RetentionPolicy.RUNTIME)
    8. public @interface PassToken {
    9.     boolean required() default true;
    10. }
    复制代码



    1. /**
    2. * 用于登录后才能操作
    3. * @author MRC
    4. * @date 2019年4月4日 下午7:02:00
    5. */
    6. @Target({ElementType.METHOD, ElementType.TYPE})
    7. @Retention(RetentionPolicy.RUNTIME)
    8. public @interface UserLoginToken {
    9.     boolean required() default true;
    10. }
    复制代码


    3.5 新建一个Server 用于下发Token

    1. /***
    2. * token 下发
    3. * @Title: TokenService.java
    4. * @author MRC
    5. * @date 2019年5月27日 下午5:40:25
    6. * @version V1.0
    7. */
    8. @Service("TokenService")
    9. public class TokenService {
    10.     public String getToken(User user) {
    11.         Date start = new Date();
    12.         long currentTime = System.currentTimeMillis() + 60* 60 * 1000;//一小时有效时间
    13.         Date end = new Date(currentTime);
    14.         String token = "";
    15.         
    16.         token = JWT.create().withAudience(user.getId()).withIssuedAt(start).withExpiresAt(end)
    17.                 .sign(Algorithm.HMAC256(user.getPassword()));
    18.         return token;
    19.     }
    20. }
    复制代码


      
    3.6 新建一个工具类 用户从token中取出用户Id

    1. 1 /*
    2. 2 * @author MRC
    3. 3 * @date 2019年4月5日 下午1:14:53
    4. 4 * @version 1.0
    5. 5 */
    6. 6 public class TokenUtil {
    7. 7
    8. 8     public static String getTokenUserId() {
    9. 9         String token = getRequest().getHeader("token");// 从 http 请求头中取出 token
    10. 10         String userId = JWT.decode(token).getAudience().get(0);
    11. 11         return userId;
    12. 12     }
    13. 13
    14. 14     /**
    15. 15      * 获取request
    16. 16      *
    17. 17      * @return
    18. 18      */
    19. 19     public static HttpServletRequest getRequest() {
    20. 20         ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
    21. 21                 .getRequestAttributes();
    22. 22         return requestAttributes == null ? null : requestAttributes.getRequest();
    23. 23     }
    24. 24 }
    复制代码


    3.7 新建一个简单的控制器 用于验证

    1. @RestController
    2. public class UserApi {
    3.     @Autowired
    4.     UserService userService;
    5.     @Autowired
    6.     TokenService tokenService;
    7.     // 登录
    8.     @GetMapping("/login")
    9.     public Object login(User user, HttpServletResponse response) {
    10.         JSONObject jsonObject = new JSONObject();
    11.         User userForBase = new User();
    12.         userForBase.setId("1");
    13.         userForBase.setPassword("123");
    14.         userForBase.setUsername("mrc");
    15.         if (!userForBase.getPassword().equals(user.getPassword())) {
    16.             jsonObject.put("message", "登录失败,密码错误");
    17.             return jsonObject;
    18.         } else {
    19.             String token = tokenService.getToken(userForBase);
    20.             jsonObject.put("token", token);
    21.             Cookie cookie = new Cookie("token", token);
    22.             cookie.setPath("/");
    23.             response.addCookie(cookie);
    24.             return jsonObject;
    25.         }
    26.     }
    27.     /***
    28.      * 这个请求需要验证token才能访问
    29.      *
    30.      * @author: MRC
    31.      * @date 2019年5月27日 下午5:45:19
    32.      * @return String 返回类型
    33.      */
    34.     @UserLoginToken
    35.     @GetMapping("/getMessage")
    36.     public String getMessage() {
    37.         // 取出token中带的用户id 进行操作
    38.         System.out.println(TokenUtil.getTokenUserId());
    39.         return "你已通过验证";
    40.     }
    41. }
    复制代码


      
    3.8 开始测试

    ## 成功登陆后保存token到前端cookie 以后的请求带上token即可区别是哪个用户的请求!

      
      
      
      
    我们下一个请求在请求的时候带上这个token试试

    成功通过验证! 我们看一下后端控制台打印的结果!

    打印出带这个token的用户  
      


      
      
    DEMO测试版本:https://gitee.com/mrc1999/springbootToken
    参考博客:https://www.jianshu.com/p/310d307e44c6
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-20 15:16 , Processed in 0.393380 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表