Spring Boot集成JWT快速入门demo

news2024/11/17 8:27:37

1.JWT是什么?

JWT,英文全称JSON Web Token:JSON网络令牌。为了在网络应用环境间传递声明而制定的一种基于JSON的开放标准(RFC 7519)。这个规范允许我们使用JWT在客户端和服务端之间传递安全可靠的信息。JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑自包含的方式,用于通信双方之间作为 JSON 对象安全地传递信息。此信息可以通过数字签名进行验证和信任。

  • 紧凑:这个字符串简洁,数据量小,传输速度快,能通过URL参数、HTTP请求提交的数据以及HTTP Header的方式进行传递。

  • 自包含:负载中包含很多信息,比如用户的ID等。别人拿到这个字符串,就能拿到这些关键的业务信息,从而避免再通过数据库查询等方式得到它们。

JWT的结构 JWT由三段信息用.连接构成的字符串。

Header.Payload.Signature 例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIn0.ihOZFzg3ZGIbBMneRy-4RMqors1P3nuO-wRJnQtTzWQ
  • Header头部

承载两部分信息:token类型和采用的加密算法

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

token类型:JWT 加密算法:HS256

  • Payload负载

存放有效信息的地方

iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: 过期时间戳(jwt的过期时间,这个过期时间必须要大于签发时间) nbf: 定义在什么时间之前,该jwt都是不可用的 iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

  • Signature签名

对头部及负载内容进行签证。采用Header中声明的算法,接收三个参数:base64编码的Header、base64编码的Payload和密钥(secret)进行运算。密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和进行验证。

2.环境搭建

参考代码仓库里面的mysql模块,这里只贴出docker-compose.yml

version: '3'
services:
  mysql:
    image: registry.cn-hangzhou.aliyuncs.com/zhengqing/mysql:5.7
    container_name: mysql_3306
    restart: unless-stopped                                   
    volumes:
      - "./mysql/my.cnf:/etc/mysql/my.cnf"
      - "./mysql/init-file.sql:/etc/mysql/init-file.sql"
      - "./mysql/data:/var/lib/mysql"
#      - "./mysql/conf.d:/etc/mysql/conf.d"
      - "./mysql/log/mysql/error.log:/var/log/mysql/error.log"
      - "./mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d" # init sql script directory -- tips: it can be excute  when `/var/lib/mysql` is empty
    environment:                        # set environment,equals docker run -e
      TZ: Asia/Shanghai
      LANG: en_US.UTF-8
      MYSQL_ROOT_PASSWORD: root         # set root password
      MYSQL_DATABASE: demo              # init database name
    ports:                              # port mappping
      - "3306:3306"

运行

docker-compose -f docker-compose.yml -p mysql5.7 up -d

初始化表

DROP TABLE IF EXISTS `jwt_user`; 
CREATE TABLE `jwt_user`(
 `id` varchar(32) CHARACTER SET utf8 NOT NULL COMMENT '用户ID',
 `username` varchar(100) CHARACTER SET utf8 NULL DEFAULT NULL COMMENT '登录账号',
 `password` varchar(255) CHARACTER SET utf8 NULL DEFAULT NULL COMMENT '密码'
)ENGINE = InnoDB CHARACTER SET = utf8 COMMENT = '用户表' ROW_FORMAT = Compact;


INSERT INTO jwt_user VALUES('1','admin','123');

3.代码工程

实验目标:实现JWT的签发和验证

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-demo</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>


    <artifactId>jwt</artifactId>


    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>




    </dependencies>
</project>

application.yaml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT
    username: root
    password: root


server:
  port: 8088

Jwt生成

package com.et.jwt.service;


import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;


import com.et.jwt.entity.User;
import org.springframework.stereotype.Service;


import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;




@Service("TokenService")


public class TokenService {
    long  outHours=1;
    public String getToken(User user) {
        Instant instant = LocalDateTime.now().plusHours(outHours).atZone(ZoneId.systemDefault()).toInstant();
        Date expire = Date.from(instant);
        String token="";
        // save information  to token ,such as: user id and Expires time
        token= JWT.create()
                .withAudience(user.getId())
                .withExpiresAt(expire)
                .sign(Algorithm.HMAC256(user.getPassword()));
        // use HMAC256 to generate token,key is user's password
        return token;
    }
}

jwt验证

package com.et.jwt.interceptor;


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;


import com.et.jwt.annotation.PassToken;
import com.et.jwt.annotation.UserLoginToken;
import com.et.jwt.entity.User;
import com.et.jwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;




public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    UserService userService;
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        //  get token  from http header
        String token = httpServletRequest.getHeader("token");
        if(!(object instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod=(HandlerMethod)object;
        Method method=handlerMethod.getMethod();
        //check is included @passtoken annotation? jump if have
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        //check is included @UserLoginToken ?
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // excute verify
                if (token == null) {
                    throw new RuntimeException("token invalid,please login again");
                }
                // get  user id  from  token
                String userId;
                try {
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                User user = userService.findUserById(userId);
                if (user == null) {
                    throw new RuntimeException("user is not exist,please login again");
                }
                // verify token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                    if (JWT.decode(token).getExpiresAt().before(new Date())) {
                        throw new RuntimeException("token Expires");
                    }


                } catch (JWTVerificationException e) {
                    throw new RuntimeException("401");
                }


                return true;
            }
        }
        return true;
    }


    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {


    }
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {


    }
}

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

  • https://github.com/Harries/springboot-demo

4.测试

  • 启动Spring Boot应用

  • 登录系统

    http://localhost:8088/api/login?username=admin&password=123

{
 "user": {
 "username": "admin",
 "password": "123",
 "id": "1"
 },
 "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIn0.ihOZFzg3ZGIbBMneRy-4RMqors1P3nuO-wRJnQtTzWQ"
}
  • 在header里面设置 token,访问

    http://localhost:8088/api/getMessage

24c66ffc012d08e554225b4dbc16e092.png

5.引用

  • https://www.v2ex.com/t/817906

  • http://www.liuhaihua.cn/archives/710374.html

  • https://github.com/ChuaWi/SpringBoot-JWT/tree/master

6.Questions

这里留2个问题给读者

  1. 过期了如何刷新token呢?保证过期时候重新登录,而不是后台自动刷新,一直不过期?

  2. 如果要实现强制下线用户功能,该如何实现呢?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1574778.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习天机04(优惠劵)

1.使用Redis和Mq优化领取优惠卷的高并发操作 实现思路&#xff1a; 因为领取优惠券的操作&#xff0c;涉及到操作db的操作很多&#xff0c;比如说查询优惠卷&#xff0c;统计已经领取的数量&#xff0c;更新已经发放的数量和新增用户券。所以了防止在高并发的情况下对我们的数…

【Java+Springboot】------ 通过JDBC+GetMapping方法进行数据select查询、多种方式传参、最简单的基本示例!

一、JDBC如何使用、PostGresql数据库 1、在pom.xml 先引用jdbc组件。 <!--jdbc--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency> 2、在pom.xml 再引用p…

Vue基础知识:路由的封装抽离,路由模块的封装抽离的好处是什么?,如何快速的引入组件,基于@指代src目录,从src目录出发找组件

如果将所有的路由配置都存放在main.js中&#xff0c;是非常有问题的&#xff0c;杂且乱。所以我们要将路由模块进行抽离&#xff0c;这样有利于&#xff1a;拆分模块&#xff0c;利于维护。大致的做法就是将路由相关的东西放到router这个文件夹的index.js中&#xff0c;而将来只…

机器学习数据预处理—统计分析方法

数据预处理 1 数据规范化 量纲&#xff0c;指将一个物理导出量用若干基本量的乘方之积表示出来的表达式。数据的比较需要关注两点——绝对数值和量纲&#xff0c;而特征间因为量纲的存在导致无法直接通过绝对数值比较大小&#xff0c;也就无法判断特征间的重要性。例如若某个…

【PyQt5篇】和子线程进行通信

文章目录 &#x1f354;使用QtDesigner进行设计&#x1f6f8;和子线程进行通信&#x1f388;运行结果 &#x1f354;使用QtDesigner进行设计 我们首先使用QtDesigner设计界面 得到代码login.ui <?xml version"1.0" encoding"UTF-8"?> <ui …

File,IO流,递归详解

File类 介绍 java.io.File类是Java语言提供了用来描述文件和目录(文件夹)的 构造 方法 注意&#xff1a; 构造方法中通常用的是第一个方法文件和目录可以通过File封装成对象File封装的对象仅仅是一个路径名&#xff0c;它是可以存在的&#xff0c;也可以不存在 绝对路径…

图片批量高效管理,图片像素缩放支持自定义操作,让图像处理更轻松

在数字化时代&#xff0c;图片管理成为了我们生活和工作中不可或缺的一部分。无论是个人用户还是企业用户&#xff0c;都需要对大量的图片进行有效的管理和处理。然而&#xff0c;面对众多的图片&#xff0c;如何进行批量管理并对其进行像素缩放成为了一个挑战&#xff0c;该如…

web学习笔记(五十三)身份认证

目录 1.Web 开发模式 1.1 服务端渲染的 Web 开发模式 1.2 服务端渲染的优缺点 1.3 前后端分离的 Web 开发模式 1.4 如何选择 Web 开发模式 2. 身份认证 2.1 Session 认证机制 3. 在 Express 中使用 Session 认证 3.1 安装express-session 中间件 3.2 配置 express-ses…

JVM内存性能调优思路之:通过GC log、Thread Dump 、Heap Dump分析内存使用说明

文章目录 一. 各日志概述1. Garbage Collection Log - 找到GC规律2. 线程转储(Thread dump) - 分析&#xff08;快照&#xff09;线程状态3. 堆转储(Heap dump) - APP某刻内存使用全貌 二. 命令1. 程序的gc日志2. 线程转储3. 堆转储 概述 在 Java 虚拟机中&#xff0c;(GC) Gar…

linux 文件提权|属性修改

文章目录 suid&#xff08;set uid&#xff09;添加文件属性查看文件属性i &#xff08;immutable&#xff09; umask suid&#xff08;set uid&#xff09; 让文件在执行的时候具有属主&#xff08;对应文件 user &#xff09;的权限 chmod 7744 temp.txt 第一位的7表示权限位…

软件自动化测试的难点怎么解决

软件自动化测试是一种使用工具或脚本来代替人工执行测试用例的方法&#xff0c;它可以提高测试的效率和质量&#xff0c;但也存在一些挑战和问题。 总结了以下几个常见的难点和解决方案&#xff1a; 接口文档的不完整性&#xff1a;有些接口的文档没有说明清楚必要的字段、参…

深入理解数据结构第三弹——二叉树(3)——二叉树的基本结构与操作

二叉树&#xff08;1&#xff09;&#xff1a;深入理解数据结构第一弹——二叉树&#xff08;1&#xff09;——堆-CSDN博客 二叉树&#xff08;2&#xff09;&#xff1a;深入理解数据结构第二弹——二叉树&#xff08;2&#xff09;——堆排序及其时间复杂度-CSDN博客 前言…

【前端Vue】Vue3+Pinia小兔鲜电商项目第4篇:静态结构搭建和路由配置,1. 准备分类组件【附代码文档】

Vue3ElementPlusPinia开发小兔鲜电商项目完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;认识Vue3&#xff0c;使用create-vue搭建Vue3项目1. Vue3组合式API体验,2. Vue3更多的优势,1. 认识create-vue,2. 使用create-vue创建项目,1. setup选项的写法和执行…

C语言解决汉诺塔问题

背景 首先带大家了解一下汉诺塔问题 汉诺塔是一个典型的函数递归问题&#xff0c;汉诺塔描述了这样的场景&#xff0c;有三个柱子&#xff0c;A,B,C&#xff0c;A柱为起始柱&#xff0c;在A柱上面有若干大小不同的盘子&#xff0c;最下面的最大&#xff0c;最上面的最小&#x…

Linux系统下安装ElasticSearch

一、228环境ES使用安装 1、检验ES服务是否安装成功的方法 &#xff08;1&#xff09;查看Elasticsearch进程是否成功 ps -ef|grep elasticsearch &#xff08;2&#xff09;linux elasticsearch下访问&#xff08;curl带认证访问&#xff09; curl --user elastic:Zhes.13…

CSS面试题常用知识总结day03

大家好我是没钱的君子下流坯&#xff0c;用自己的话解释自己的知识 前端行业下坡路&#xff0c;甚至可说前端已死&#xff0c;我还想在前段行业在干下去&#xff0c;所以从新开始储备自己的知识。 从CSS——>Javascript——>VUE2——>Vuex、VueRouter、webpack——>…

STM32F103C8T6-CAN

本文内容 HAL库下printf重定向解决问题&#xff1a;Keil下调试可以正常运行&#xff0c;而下载后运行不了CAN总线的回环测试&#xff0c;自发自收 printf重定向 实现printf重定向的目的是方便调试&#xff0c;通过UART查看打印的调试信息。 下面以STM32F103C8T6为例&#xf…

【前沿模型解析】潜在扩散模 1 | LDM第一阶段-感知图像压缩总览

文章目录 0 开始~1 感知压缩的目的2 自回归编码器-解码器生成模型一览2.1 AE 自编码器2.2 VAE 变分自编码器2.3 VQ-VAE2.4 VQ-GAN 3 代码部分讲解总览 0 开始~ 从今天起呢&#xff0c;我们会剖析LDM&#xff08;潜在扩散模型&#xff09; 从去年开始&#xff0c;大量的生成模…

数学建模-------MATLAB分支循环断点调试

1.if语句 &#xff08;1&#xff09;分段函数的引入&#xff08;这里的数据表示的是分数的不同区间对应的等级&#xff09; (1)这个就是一个十分简单的if语句&#xff0c;无论是if还是elseif后面都是不能添加任何分号的&#xff0c;这个例子就是一个分段的函数&#xff0c;在不…

基础总结篇:Activity生命周期

private int param 1; //Activity创建时被调用 Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG, “onCreate called.”); setContentView(R.layout.lifecycle); Button btn (Button) findViewById(R.id.…