SpringBoot 整合JWT实现基于自定义注解的-登录请求验证拦截(保姆级教学,附:源码)1

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: SpringBoot 整合JWT实现基于自定义注解的-登录请求验证拦截

学习目标:

Spring Boot 整合JWT实现基于自定义注解的 登录请求接口拦截

例:

  • 一篇掌握 JWT 入门知识

1.1 在学习SpringBoot 整合JWT之前,我们先来说说JWT进行用户身份验证的流程

  • 1:客户端使用用户名和密码请求登录

     2:服务端收到请求,验证用户名和密码

     3:验证成功后,服务端会签发一个token,再把这个token返回给客户端

     4:客户端收到token后可以把它存储起来,比如放到cookie中

     5:客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带

     6:服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据

1.2 而JWT就是上述流程当中token的一种具体实现方式,其全称是JSON Web Token,官网地址:https://jwt.io/

并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。JWT的认证流程如下:

  • 1:首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探

     2:后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串

     3:后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可

     4:前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)

后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等

    5:验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

cc224ccab0a9471eb141550b817627d5.png

1.3  JWT结构

JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串

JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

0b8e7f197f7046da8fac44d70d22dfb9.png

1.3.1 Header

JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存

{
  "alg": "HS256",
  "typ": "JWT"
}

1.3.2 Payload

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如下例:

{
  "sub": "1234567890",
  "name": "Helen",
  "admin": true
}

请注意:默认情况下JWT是未加密的,因为只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT中,以防止信息泄露。JWT只是适合在网络中传输一些非敏感的信息

JWT 解码工具:在线JWT Token解析解码工具_x@lijun的博客-CSDN博客

1.3.3 Signature

签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名

HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象

6550433e52074ec0886805039ef3ae99.png

  • 注意JWT每部分的作用,在服务端接收到客户端发送过来的JWT token之后:

   header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据

   signature由于使用了不可逆的加密算法,无法解码出原文,它的作用是校验token有没有被篡改。服务端获取header中的加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一致。注意secretKey只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,secretKey实际上代表的是盐值

SpringBoot 整合JWT实现基于自定义注解的-实现一个登录请求验证拦截

1:创建数据库结构

CREATE TABLE `users` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

10c48742774c4671b1fbb71993ce0790.png

2: 创建SpringBoot(2.3.5.RELEASE版本)工程,引入pom依赖

pom.xml

<!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- MP -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- jwt -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>
        <!--  hutool -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

3: application.properties 文件

server.port=8900
spring.application.name=jwt
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.log4j2.Log4j2Impl
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/peixun?serverTimezone=GMT%2B8
spring.datasource.username=root

4:JWTUtil 工具类

public class JWTUtils {
    private static final String APP_SECRET  = "ukc8BDbRigUDaY6pZFfWus2jZWLPHOsdadasdasfdssfeweee";
    /**
     * 生成token
     * @param map  传入payload
     * @return 返回token
     */
    public static String getToken(Map<String,String> map){
        Calendar instance = Calendar.getInstance();
//        instance.add(Calendar.MILLISECOND,20);
        instance.add(Calendar.DAY_OF_WEEK,7);
        // 创建 JWT builder
        JWTCreator.Builder builder = JWT.create();
        // payload
        map.forEach(builder::withClaim);
        // 设置过期时间
        builder.withExpiresAt(instance.getTime());
        // 设置签名加密
        String token = builder.sign(Algorithm.HMAC256(APP_SECRET));
        return token;
    }
    /**
     * 验证token
     * @param token
     * @return
     */
    public static void verify(String token){
        JWT.require(Algorithm.HMAC256(APP_SECRET)).build().verify(token);
    }
    /**
     * 获取token中payload
     * @param token
     * @return
     */
    public static DecodedJWT getToken(String token){
        return JWT.require(Algorithm.HMAC256(APP_SECRET)).build().verify(token);
    }
}

5:封装统计返回结果类

Result

public class Result implements Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    //状态码
    private Integer code;
    //响应消息
    private String msg;
    //响应数据
    private Object data;
    private Integer count;
    public Integer getCount() {
        return count;
    }
    public void setCount(Integer count) {
        this.count = count;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public Result(ResultCode resultCode,Object data) {
        this.code=resultCode.getCode();
        this.msg=resultCode.getMsg();
        this.data=data;
    }
    public Result(Integer code,String msg,Object data) {
        this.code=code;
        this.msg=msg;
        this.data=data;
    }
}

ResultCode

public enum ResultCode {
    //成功
    SUCCESS(200,"成功"),
    PARAM_IS_INVALID(1001,"参数无效"),
    PARAM_IS_BLANK(1002,"参数为空"),
    PARAM_IS_ERROR(1004,"参数错误"),
    PARAM_IS_COMPLETE(1003,"参数缺失"),
    USER_LONGIN_ERROR(2001,"账号或密码错误"),
    USER_LONGIN_EXIST(2002,"用户不存在"),
    USER_LONGIN_EXISTD(2003,"用户已存在"),
    KHXX_EXISTD(2010,"客户信息已存在!"),
    USER_LONGIN_EXAMINE(2004,"用户待审核"),
    UPDATE_EXAMINE(2006,"修改申请已提交,等待管理员审核"),
    USER_LONGIN_STOP(2005,"用户已停用"),
    ERROR(500,"操作失败"),
    CUSTOMER_EXISTD(400,"客户信息已存在"),
    PHONE_ERROR(402,"验证码或者手机号错误"),
    PHONE_EXIST(405,"手机号为空"),
    PHONE_RETRY(405,"请2分钟后重试"),
    PHONE_FPRMATEXIST(406,"手机号错误"),
    SINE_ERROR(401,"用户信息已过期,请重新登录"),
    LONG_OVERTIME(406,"登录超时,请重新登录"),
    KHXX_HTJH_EXISTD(1005,"该类型的计划已存在"),
    USER_INSUFFICIENT_AUTHORITY(403,"权限不足"),
    FILE_NOTBLANK(2007,"上传文件不能为空"),
    FILE_MAXSIZE(2008,"上传文件不能超过2M"),
    FILE_ERRORSUFFIX(2009,"上传文件错误,只能上传文档或表格以及PDF格式");
    private Integer code;
    private String msg;
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    ResultCode(Integer code,String msg){
        this.code=code;
        this.msg=msg;
    }
}


目录
相关文章
|
6天前
|
IDE Java Maven
Springboot中Processor注解概念以及实战案例
【5月更文挑战第28天】在Spring Boot中,没有直接名为Processor的注解。不过,你可能是在谈论与Spring Boot相关的注解处理器(Annotation Processors)的概念,尤其是在处理自定义注解或@ConfigurationProperties注解时的情况。
18 1
|
17天前
|
Java
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
|
19天前
|
前端开发 Java
基于SpringBoot实现登录注册
使用SpringBoot实现的登录注册功能,前端负责获取用户信息并发送至后端,后端接收到数据后进行持久化处理。
20 0
|
19天前
|
Java 关系型数据库 MySQL
保姆级教程——将springboot项目部署到阿里云服务器包含环境配置(小白包会)
本文档详细介绍了将SpringBoot项目部署到阿里云服务器的步骤。首先,通过Xshell连接服务器,使用公网IP地址。接着,下载JDK的Linux版本,使用XFTP上传并解压,配置环境变量。然后,安装MySQL 5.7,包括下载YUM源、安装、启动服务以及修改root密码和开启远程访问。最后,将SpringBoot项目打包成jar,上传至服务器,使用`java -jar`命令运行,通过`nohup`确保服务持续运行。配置安全组以允许远程访问。
|
19天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
27 2
|
19天前
|
前端开发 Java
SpringBoot之自定义注解参数校验
SpringBoot之自定义注解参数校验
25 2
|
19天前
|
安全 关系型数据库 MySQL
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
33 3
|
19天前
|
Java Spring 容器
SpringBoot自动装配原理之@Import注解解析
SpringBoot自动装配原理之@Import注解解析
56 0
|
2天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的漫画阅读系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的漫画阅读系统的详细设计和实现(源码+lw+部署文档+讲解等)
|
2天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的快递信息管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的快递信息管理系统的详细设计和实现(源码+lw+部署文档+讲解等)