当前位置:   article > 正文

SpringBoot集成Token

springboot集成token

简介

在项目开发中,Token 是常见且重要的一个功能,目前对于 Token 设计有很多成熟的方案;比如使用 Redis 存储管理 Token,不过这种方式需要而额外集成 Redis 服务,虽然 Redis 查询效率很高,但是对于普通项目来说,还是增加了开发难度;本章将介绍一个简单易用的 Token 插件:jjwt,该插件直接与 SpringBoot 集成即可,其原理是:在服务端加密生成一个三段式加密字符串,前端每次请求都要将该 Token 传递给服务端(建议放在 Header 中),后台通过解析该 Token 字符串,判断其是否合法,超时等

基本原理

从这个架构图中可以看出 JWT 主体分为 3 个部分:user,application server,authentication server;
非常常见的一个架构,首先用户需要 通过登录等手段向 authentication server 发送一个认证请求,authentication会返回给用户一个 JWT (这个JWT 的具体内容格式是啥后面会说,先理解成一个简单的字符串好了) 此后用户向application server发送的所有请求都要捎带上这个 JWT,然后application server 会验证这个 JWT 的合法性,验证通过则说明用户请求时来自合法守信的客户端

JWT 结构

这个 JWT 的格式,就是一个由三部分组成的字符串:header.payload.signatue;其中 header 主要包含了加密算法等信息;payload 则主要包含后端服务器放入的自定义信息,如:登录用户的信息;signature 就是使用算法生成的能够实现身份认证的字符串

实现步骤

1. 在项目的 pom.xml 配置文件中添加如下依赖

  1. <dependency>
  2. <groupId>io.jsonwebtoken</groupId>
  3. <artifactId>jjwt</artifactId>
  4. <version>0.7.0</version>
  5. </dependency>

 2. 添加一个公共类 Token,提供生成 Token,校验等基本方法

  1. /**
  2. * @ClassName Token
  3. * @Author Andy
  4. * @Date 2020/7/14 14:32
  5. * @Description 用于生成, 解析 token 的工具类
  6. **/
  7. public class Token {
  8. // 密钥
  9. private static SecretKeySpec key = "";
  10. // 对密钥加密
  11. static {
  12. key = new SecretKeySpec(Constant.SECRET_KEY.getBytes(), SignatureAlgorithm.HS512.getJcaName());
  13. }
  14. // 生成 token
  15. public static String createToken(String subject, Map < String, Object > claims, Date expireDate) {
  16. JwtBuilder builder = Jwts.builder()
  17. .setClaims(claims) // payload 私有申明,存放一些个人信息,必须放在第一个
  18. .setIssuer(Constant.AUTHOR) // token 的签发人
  19. .setIssuedAt(new Date()) // token 的签发时间
  20. .setSubject(subject) // token 的所有人, 一般放用户的 id 之类的
  21. .setExpiration(expireDate) // 过期时间
  22. .signWith(SignatureAlgorithm.HS512, key); // token 的签名算法
  23. return builder.compact();
  24. }
  25. // 生成 token
  26. public static String createToken(String subject, Date expireDate) {
  27. return createToken(subject, new HashMap < > (), expireDate);
  28. }
  29. // 解析 token
  30. public static Claims parseToken(String token) {
  31. try {
  32. return Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
  33. } catch (Exception e) {
  34. throw new CommonException(Exceptions.TOKEN_PARSE_ERROR);
  35. }
  36. }
  37. // 将 token 标记为过期
  38. public static void markTokenExpired(String token) {
  39. Date expireDate = CommonUtil.currentTimeAddSeconds(1);
  40. Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().setExpiration(expireDate);
  41. }
  42. // 判断 token 是否过期
  43. public static boolean checkTokenExpired(String token) {
  44. Claims claims = parseToken(token);
  45. return !CommonUtil.checkExpired(claims.getExpiration());
  46. }
  47. // 获取 token 的 subject 信息, 登录成功保存的时用户的主键
  48. public static String getSubject(HttpServletRequest request) {
  49. return parseToken(getToken(request)).getSubject();
  50. }
  51. // 根据 token 获取 subject 信息
  52. public static String getSubject(String token) {
  53. return parseToken(token).getSubject();
  54. }
  55. // 根据 HttpServletRequest 获取 token
  56. public static String getToken(HttpServletRequest request) {
  57. return request.getHeader(Constant.HEADER_TOKEN);
  58. }
  59. // 获取 token 的签发人
  60. public static String getIssuer(String token) {
  61. return parseToken(token).getIssuer();
  62. }
  63. }

这里使用到其它关联类的方法或属性如下

Constant.class

  1. // token 签发者
  2. public static final String AUTHOR = "duzimei";
  3. // 获取 header 中 token 标识
  4. public static final String HEADER_TOKEN = "token";
  5. // token 密钥加密字符串(取自 《肖申克的救赎》经典台词)
  6. public static final String SECRET_KEY = "Fear can hold you prisoner.Hope can set you free";
  7. // 设置 token 的过期时间为 1 个小时(单位秒)
  8. public static final Integer EXPIRE_DATE = 3600;

CommonUtil.class

  1. // 在当前时间基础上加 seconds 秒
  2. public static Date currentTimeAddSeconds(int seconds) {
  3. Calendar now = Calendar.getInstance();
  4. now.add(Calendar.SECOND, seconds);
  5. return now.getTime();
  6. }
  7. // 判断于当前日期是否过期
  8. public static boolean checkExpired(Date expiration) {
  9. return expiration.after(new Date());
  10. }

异常定义请忽略,根据自己项目而实现

3. 定义一个 UserController.class,添加登录方法,当用户登录成功后,返回一个 Token

  1. @RestController
  2. @RequestMapping("/user")
  3. public class User {
  4. @Autowired
  5. private UserService userService;
  6. @PostMapping("/login")
  7. public Map<Integer, String> login(@RequestBody JSONObject params) {
  8. Map<Integer, String> result = new HashMap<>();
  9. String userAccount = params.getString("account");
  10. String password = params.getString("password");
  11. User tempUser = userService.getUserByAccount(userAccount);
  12. if(null == tempUser) {
  13. result.put(-1, "用户不存在");
  14. } else if(!password.equals(tempUser.getPassword())) {
  15. result.put("-2", "密码不正确");
  16. } else {
  17. // 用户登录成功, 添加 token 返回给客户端
  18. // 1. 设置 token 过期时间
  19. Date expireDate = CommonUtil.currentTimeAddSeconds(Contant.EXPIRE_DATE);
  20. // 2. 这里我们把用户的 id 信息放到 subject 中
  21. String token = Token.createToken(tempUser.getUserId(), expireDate);
  22. result.put(0, token);
  23. }
  24. return result;
  25. }
  26. }

4. 前端当用户登录成功后,向后台发送其它请求的时候,将 Token 放到 Header 中一起传送给后台

  1. $.ajax({
  2. headers: {
  3. "token": 从后台获取到的token
  4. },
  5. url: "http://xxx:8080/user/info",
  6. method: "GET",
  7. data: null,
  8. dataType: "json",
  9. contentType: "application/json",
  10. success: function(data) {
  11. console.log(data);
  12. },
  13. error: function(x, s, e) {
  14. console.log("异常信息: " + x.responseText);
  15. }
  16. })

5.解析Token

在后台对应方法中,就可以通过解析 Token 获取用户 id,判断请求是否合法;像下面这种请求,前端根本不用传递用户的 id 到后台,后台通过解析 Token,获取 Token 的 subject 属性就能拿到用户 id,在一定程度上提高了安全性

  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4. @Autowired
  5. private UserService userService;
  6. @GetMapping("/info")
  7. public Map<Integer, Object> getUserInfo(HttpServletRequest request) {
  8. Map<Integer, Object> result = new HashMap<>();
  9. String token = Token.getToken(request);
  10. // 对 token 进行校验
  11. if(StringUtils.isEmpty(token)) { // 如果没有获取到 Token
  12. result.put(-1, "没有获取到 Token, 请求被阻止");
  13. } else if(!Constant.AUTHOR.equals(Token.getIssuer(token))) { // 如果 Token 的签发人不正确
  14. result.put(-2, "非法的 Token, 请求被阻止");
  15. } else if(Token.checkTokenExpired(token)) { // 如果 Token 已过期
  16. result.put(-3, "过期的 Token, 请求被阻止");
  17. } else {
  18. String userId = Token.getSubject(token);
  19. User user = userService.getUserById(userId);
  20. result.put(0, user);
  21. }
  22. return result;
  23. }
  24. }

这里只是介绍的关于 JWT 实现 Token 的简单用法,在实际开发过程中,建议使用 Spring 的 AOP 技术实现对 Token 的校验;我们这里只是实现了一个最简单的 Token,这 Token 中还可以放入其它信息,由于是单向加密的,所以数据传输非常安全;更进一步的实现,应根据实际开发具体实现

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/368029
推荐阅读
相关标签
  

闽ICP备14008679号