• JWT登录验证



    目录

    一、JWT是什么?

    1.概念

    2.结构

    二、书写工具(config)包

    1.JwtUtils

    2.JsonResult

    3.ResultCode

    4.ResultTool

    三、Controller层


    一、JWT是什么?

    1.概念

    JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。是由用户以用户名、密码登录,服务端验证后,会生成一个 token,返回给客户端,客户端在下次访问的过程中携带这个 token,服务端责每次验证这个token。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。

    2.结构

    • 标头(Header)
    • 有效载荷(Payload)
    • 签名(Signature)

    二、书写工具(config)包

    在SpringBoot项目下创建config(工具包)包,在包底下创建一下类和枚举。

    1.JwtUtils

    1. package com.alanx.java.util;
    2. import com.alanx.java.bean.Users;
    3. import io.jsonwebtoken.Claims;
    4. import io.jsonwebtoken.Jws;
    5. import io.jsonwebtoken.Jwts;
    6. import io.jsonwebtoken.SignatureAlgorithm;
    7. import org.springframework.util.StringUtils;
    8. import javax.servlet.http.HttpServletRequest;
    9. import java.util.Date;
    10. public class JwtUtils {
    11. //常量
    12. public static final long EXPIRE = 1000 * 60 * 60 * 24; //token过期时间
    13. public static final String APP_SECRET = "13579"; //秘钥,加盐
    14. // @param id 当前用户ID
    15. // @param issuer 该JWT的签发者,是否使用是可选的
    16. // @param subject 该JWT所面向的用户,是否使用是可选的
    17. // @param ttlMillis 什么时候过期,这里是一个Unix时间戳,是否使用是可选的
    18. // @param audience 接收该JWT的一方,是否使用是可选的
    19. //生成token字符串的方法
    20. public static String getJwtToken(Users user) {
    21. String JwtToken = Jwts.builder()
    22. .setHeaderParam("typ", "JWT") //头部信息
    23. .setHeaderParam("alg", "HS256") //头部信息
    24. //下面这部分是payload部分
    25. // 设置默认标签
    26. .setSubject("alanx") //设置jwt所面向的用户
    27. .setIssuedAt(new Date()) //设置签证生效的时间
    28. .setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) //设置签证失效的时间
    29. //自定义的信息,这里存储id和姓名信息
    30. .claim("id", user.getId()) //设置token主体部分 ,存储用户信息
    31. .claim("name", user.getUserName())
    32. .claim("nickName",user.getNickName())
    33. //下面是第三部分
    34. .signWith(SignatureAlgorithm.HS256, APP_SECRET)
    35. .compact();
    36. // 生成的字符串就是jwt信息,这个通常要返回出去
    37. return JwtToken;
    38. }
    39. /**
    40. * 判断token是否存在与有效
    41. * 直接判断字符串形式的jwt字符串
    42. *
    43. * @param jwtToken
    44. * @return
    45. */
    46. public static boolean checkToken(String jwtToken) {
    47. if (StringUtils.isEmpty(jwtToken)) return false;
    48. try {
    49. Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
    50. } catch (Exception e) {
    51. e.printStackTrace();
    52. return false;
    53. }
    54. return true;
    55. }
    56. /**
    57. * 判断token是否存在与有效
    58. * 因为通常jwt都是在请求头中携带,此方法传入的参数是请求
    59. *
    60. * @param request
    61. * @return
    62. */
    63. public static boolean checkToken(HttpServletRequest request) {
    64. try {
    65. String jwtToken = request.getHeader("token");//注意名字必须为token才能获取到jwt
    66. if (StringUtils.isEmpty(jwtToken)) return false;
    67. Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
    68. } catch (Exception e) {
    69. e.printStackTrace();
    70. return false;
    71. }
    72. return true;
    73. }
    74. /**
    75. * 根据token字符串获取会员id
    76. * 这个方法也直接从http的请求中获取id的
    77. *
    78. * @param request
    79. * @return
    80. */
    81. public static String getMemberIdByJwtToken(HttpServletRequest request) {
    82. String jwtToken = request.getHeader("token");
    83. if (StringUtils.isEmpty(jwtToken)) return "";
    84. Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
    85. Claims claims = claimsJws.getBody();
    86. return (String) claims.get("id");
    87. }
    88. /**
    89. * 解析JWT
    90. *
    91. * @param jwt
    92. * @return
    93. */
    94. public static Claims parseJWT(String jwt) {
    95. Claims claims = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwt).getBody();
    96. return claims;
    97. }
    98. }

    2.JsonResult

    1. package com.alanx.java.util;
    2. import lombok.Data;
    3. import java.io.Serializable;
    4. /**
    5. * @Description: 统一返回实体
    6. */
    7. @Data
    8. public class JsonResult<T> implements Serializable {
    9. private Boolean success;
    10. private Integer errorCode;
    11. private String errorMsg;
    12. private T data;
    13. public JsonResult() {
    14. }
    15. public JsonResult(boolean success) {
    16. this.success = success;
    17. this.errorCode = success ? ResultCode.SUCCESS.getCode() : ResultCode.COMMON_FAIL.getCode();
    18. this.errorMsg = success ? ResultCode.SUCCESS.getMessage() : ResultCode.COMMON_FAIL.getMessage();
    19. }
    20. public JsonResult(boolean success, ResultCode resultEnum) {
    21. this.success = success;
    22. this.errorCode = success ? ResultCode.SUCCESS.getCode() : (resultEnum == null ? ResultCode.COMMON_FAIL.getCode() : resultEnum.getCode());
    23. this.errorMsg = success ? ResultCode.SUCCESS.getMessage() : (resultEnum == null ? ResultCode.COMMON_FAIL.getMessage() : resultEnum.getMessage());
    24. }
    25. public JsonResult(boolean success, T data) {
    26. this.success = success;
    27. this.errorCode = success ? ResultCode.SUCCESS.getCode() : ResultCode.COMMON_FAIL.getCode();
    28. this.errorMsg = success ? ResultCode.SUCCESS.getMessage() : ResultCode.COMMON_FAIL.getMessage();
    29. this.data = data;
    30. }
    31. public JsonResult(boolean success, ResultCode resultEnum, T data) {
    32. this.success = success;
    33. this.errorCode = success ? ResultCode.SUCCESS.getCode() : (resultEnum == null ? ResultCode.COMMON_FAIL.getCode() : resultEnum.getCode());
    34. this.errorMsg = success ? ResultCode.SUCCESS.getMessage() : (resultEnum == null ? ResultCode.COMMON_FAIL.getMessage() : resultEnum.getMessage());
    35. this.data = data;
    36. }
    37. }

    3.ResultCode

    1. package com.alanx.java.util;
    2. /**
    3. * @Description: 返回码定义
    4. * 规定:
    5. * #1表示成功
    6. * #1001~1999 区间表示参数错误
    7. * #2001~2999 区间表示用户错误
    8. * #3001~3999 区间表示接口异常
    9. */
    10. public enum ResultCode {
    11. /* 成功 */
    12. SUCCESS(200, "成功"),
    13. /* 默认失败 */
    14. COMMON_FAIL(999, "失败"),
    15. /* 参数错误:1000~1999 */
    16. PARAM_NOT_VALID(1001, "参数无效"),
    17. PARAM_IS_BLANK(1002, "参数为空"),
    18. PARAM_TYPE_ERROR(1003, "参数类型错误"),
    19. PARAM_NOT_COMPLETE(1004, "参数缺失"),
    20. /* 用户错误 */
    21. USER_NOT_LOGIN(2001, "用户未登录"),
    22. USER_ACCOUNT_EXPIRED(2002, "账号已过期"),
    23. USER_CREDENTIALS_ERROR(2003, "密码错误"),
    24. USER_CREDENTIALS_EXPIRED(2004, "密码过期"),
    25. USER_ACCOUNT_DISABLE(2005, "账号不可用"),
    26. USER_ACCOUNT_LOCKED(2006, "账号被锁定"),
    27. USER_ACCOUNT_NOT_EXIST(2007, "账号不存在"),
    28. USER_ACCOUNT_ALREADY_EXIST(2008, "账号已存在"),
    29. USER_ACCOUNT_USE_BY_OTHERS(2009, "账号下线"),
    30. /* 业务错误 */
    31. NO_PERMISSION(3001, "没有权限");
    32. private Integer code;
    33. private String message;
    34. ResultCode(Integer code, String message) {
    35. this.code = code;
    36. this.message = message;
    37. }
    38. public Integer getCode() {
    39. return code;
    40. }
    41. public void setCode(Integer code) {
    42. this.code = code;
    43. }
    44. public String getMessage() {
    45. return message;
    46. }
    47. public void setMessage(String message) {
    48. this.message = message;
    49. }
    50. /**
    51. * 根据code获取message
    52. *
    53. * @param code
    54. * @return
    55. */
    56. public static String getMessageByCode(Integer code) {
    57. for (ResultCode ele : values()) {
    58. if (ele.getCode().equals(code)) {
    59. return ele.getMessage();
    60. }
    61. }
    62. return null;
    63. }
    64. }

    4.ResultTool

    1. package com.alanx.java.util;
    2. /**
    3. * @Description: 返回体构造工具
    4. */
    5. public class ResultTool {
    6. public static JsonResult success() {
    7. return new JsonResult(true);
    8. }
    9. public static <T> JsonResult<T> success(T data) {
    10. return new JsonResult(true, data);
    11. }
    12. public static JsonResult fail() {
    13. return new JsonResult(false);
    14. }
    15. public static JsonResult fail(ResultCode resultEnum) {
    16. return new JsonResult(false, resultEnum);
    17. }
    18. }

    三、Controller层

    首次登录成功后,将信息存放到Rides中

    1. package com.alanx.java.controller;
    2. import com.alanx.java.bean.Users;
    3. import com.alanx.java.service.UserService;
    4. import com.alanx.java.util.JsonResult;
    5. import com.alanx.java.util.JwtUtils;
    6. import com.alanx.java.util.ResultTool;
    7. import com.alibaba.fastjson.JSON;
    8. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    9. import org.springframework.data.redis.core.StringRedisTemplate;
    10. import org.springframework.web.bind.annotation.PostMapping;
    11. import org.springframework.web.bind.annotation.RequestMapping;
    12. import org.springframework.web.bind.annotation.RestController;
    13. import javax.annotation.Resource;
    14. import java.util.List;
    15. import java.util.concurrent.TimeUnit;
    16. @RestController
    17. @RequestMapping("/users")
    18. public class UsersController {
    19. @Resource
    20. private UserService service;
    21. @Resource
    22. private StringRedisTemplate stringRedisTemplate;
    23. @PostMapping("/login")
    24. public String login(Users users) {
    25. QueryWrapper<Users> wrapper = new QueryWrapper<>();
    26. wrapper.eq("username", users.getUserName());
    27. wrapper.eq("password", users.getPassWord());
    28. List<Users> list = service.list(wrapper);
    29. if (list.size() == 0) {
    30. return JSON.toJSONString(ResultTool.fail()); //失败了返回一个错误代码
    31. }
    32. Users u = list.get(0);
    33. String token = JwtUtils.getJwtToken(u);
    34. stringRedisTemplate.opsForValue().set(u.getId() + "", token, 1, TimeUnit.DAYS); //存放到redis中
    35. JsonResult<String> result = ResultTool.success(token);
    36. return JSON.toJSONString(result);
    37. }
    38. }

     使用Postman测试一下结果密码错误会返回一个错误代码

    密码正确的话,会出现jwt

     在Redis中查询结果,复制结果去​​​​​​​JWT官网进行解码后就会出现除密码外的信息。

     

    在第二部分载体中可以自定义解码出来的结果,在JwtUtils类中的getJwtToken方法中加入信息


  • 相关阅读:
    客户成功体系如何构建?请看这7步
    vscode 运行 java 项目之解决“Build failed, do you want to continue”的问题
    几道web题目
    Nginx 代理sftp,访问Nginx服务器就间接访问sftp服务器
    Python常见函数
    taskAffinity详解
    实战分析Java的异步编程,并通过CompletableFuture进行高效调优
    ChatGPT推荐最常用的自动化测试、性能、安全测试工具!
    哪些人适合学习Python?
    libssl.so.10: cannot open shared object file: No such file or directory
  • 原文地址:https://blog.csdn.net/weixin_55166132/article/details/125492824