Spring Boot集成JWT快速入门demo

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. 如果要实现强制下线用户功能,该如何实现呢?

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

cuda cudnn pytorch 的下载方法(anaconda)

文章目录 前言cuda查看当前可支持的最高cuda版本显卡驱动更新下载cuda cudnnpytorch配置虚拟环境创建虚拟环境激活虚拟环境 1.直接下载2.conda 下载(清华源&#xff0c;下载速度慢的看过来)添加清华镜像channel下载下载失败 下载失败解决办法1.浑水摸鱼&#xff0c;风浪越大鱼越…

记一次 .NET某管理局检测系统 内存暴涨分析

一&#xff1a;背景 1. 讲故事 前些天有位朋友微信找到我&#xff0c;说他们的WPF程序有内存泄漏的情况&#xff0c;让我帮忙看下怎么回事&#xff1f;并且dump也抓到了&#xff0c;网上关于程序内存泄漏&#xff0c;内存暴涨的文章不计其数&#xff0c;看样子这个dump不是很…

阿里云服务器可以干嘛?能干啥你还不知道么!

阿里云服务器可以干嘛&#xff1f;能干啥你还不知道么&#xff01;简单来讲可用来搭建网站、个人博客、企业官网、论坛、电子商务、AI、LLM大语言模型、测试环境等&#xff0c;阿里云百科aliyunbaike.com整理阿里云服务器的用途&#xff1a; 阿里云服务器活动 aliyunbaike.com…

[大模型]大语言模型量化方法对比:GPTQ、GGUF、AWQ

在过去的一年里&#xff0c;大型语言模型(llm)有了飞速的发展&#xff0c;在本文中&#xff0c;我们将探讨几种(量化)的方式&#xff0c;除此以外&#xff0c;还会介绍分片及不同的保存和压缩策略。 说明&#xff1a;每次加载LLM示例后&#xff0c;建议清除缓存&#xff0c;以…

【Java设计模式】创建型——抽象工厂模式

目录 背景/问题解决方案&#xff1a;抽象工厂模式解析生活场景模拟上一章的案例图解 意图主要解决何时使用如何解决关键代码抽象工厂模式涉及多个角色&#xff1a; 代码示例优点缺点应用场景 背景/问题 在某些情况下&#xff0c;需要创建一系列相关或相互依赖的对象&#xff0…

一些Java面试题

1、 Java语言有哪些特点 1、简单易学、有丰富的类库 2、面向对象&#xff08;Java最重要的特性&#xff0c;让程序耦合度更低&#xff0c;内聚性更高&#xff09; 3、与平台无关性&#xff08;JVM是Java跨平台使用的根本&#xff09; 4、可靠安全 5、支持多线程 2、面向对象和…

07 Python进阶:多线程

python线程概念 在 Python 中&#xff0c;线程&#xff08;Thread&#xff09;是用于实现多任务并发执行的基本单元。线程允许程序同时执行多个部分&#xff0c;每个部分称为一个线程&#xff0c;因此能够提高程序的效率&#xff0c;特别适用于需要同时执行多个任务的情况。下面…

StarRocks实战——华米科技埋点分析平台建设

目录 前言 一、原有方案及其痛点 二、引入StarRocks 三、方案改造 3.1 架构设计 3.2 数据流程 3.3 性能指标 3.4 改造收益 前言 华米科技是一家基于云的健康服务提供商&#xff0c;每天都会有海量的埋点数据&#xff0c;以往基于HBase建设的埋点计算分析项目往往效率上…

2024.4.2-day07-CSS 盒子模型(显示模式、盒子模型)

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 作业 2024.4.2 学习笔记CSS标签元素显示模式1 块元素2 行内元素3 行内块元素4…

Linux | MySQL安装Workbench图形化

环境:rhel8 MySQL8 下载软件包 官网软件包地址&#xff1a; MySQL &#xff1a;&#xff1a; 下载 MySQL Workbenchhttps://dev.mysql.com/downloads/workbench/我这里下载的是 mysql-workbench-community-8.0.24-1.el8.x86_64.rpm 解决依赖 用rpm安装发现缺少依赖 [rooth…

3dmax经常染失败?优化方法提升染质量!

在三维建模和渲染的过程中&#xff0c;优化模型和场景的效率是至关重要的。以下是一些提升效率的方法&#xff1a; 模型简化&#xff1a;在创建模型时&#xff0c;应尽量减少使用的命令和修改器的数量。这是因为命令和修改器越多&#xff0c;消耗的内存和CPU资源也就越多&…

FJSP:巨型犰狳优化算法(Giant Armadillo Optimization,GAO)求解柔性作业车间调度问题(FJSP),提供MATLAB代码

一、柔性作业车间调度问题 柔性作业车间调度问题&#xff08;Flexible Job Shop Scheduling Problem&#xff0c;FJSP&#xff09;&#xff0c;是一种经典的组合优化问题。在FJSP问题中&#xff0c;有多个作业需要在多个机器上进行加工&#xff0c;每个作业由一系列工序组成&a…

大米自动化生产线设备:现代粮食加工的核心力量

随着科技的不断进步和粮食加工行业的快速发展&#xff0c;大米自动化生产线设备在现代粮食加工中的地位愈发重要。这些设备不仅大大提高了生产效率&#xff0c;还保证了产品的质量和安全&#xff0c;成为了现代粮食加工行业不可或缺的核心力量。 一、自动化生产线设备助力效率提…

达托机器人(DRB)平台的安全性和前景是否可靠?

在当今数字化时代&#xff0c;技术创新不仅是企业成功的关键&#xff0c;也是整个行业的驱动力。在这个背景下&#xff0c;达托机器人&#xff08;DRB&#xff09;脱颖而出&#xff0c;以其创世团队的坚实基础和平台的可靠前景&#xff0c;引起了业界的广泛关注。 首先&#xf…

消息队列MQ(面试题:为什么使用MQ)

一、什么是mq? MQ全称 Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信&#xff0c;解耦。 二、常见的mq产品 RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMq RabbitMQ: One broker …

人工智能、深度伪造和数字身份:企业网络安全的新前沿

深度伪造&#xff08;Deepfakes&#xff09;的出现打响了网络安全军备竞赛的发令枪。对其影响的偏执已经波及到一系列领域&#xff0c;包括政治错误信息、假新闻和社交媒体操纵。 深度伪造将加剧公共领域对信任和沟通的本已严峻的压力。这将理所当然地引起监管机构和政策制定者…

java.lang.NoClassDefFoundError: javax/validation/constraints/Min

1、报错截图 2、解决办法 添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency>

浮点数在内存中的存储【详解】

浮点数在内存中的存储 浮点数存储规则小数点后数值的二进制转换float和double存储图示优化存储方案E不全为0或不全为1E全为0E全为1 浮点数存储规则 大家都知道整型数据是以补码的方式存放在内存中。以下几个概念是需要知道的&#xff1a; 原码&#xff0c;补码&#xff0c;反…

C++入门语法(命名空间缺省函数函数重载引用内联函数nullptr)

目录 前言 1. 什么是C 2. C关键字 3. 命名空间 3.1 命名空间的定义 3.2 命名空间的使用 4. C输入和输出 5. 缺省函数 5.1 概念 5.2 缺省参数分类 6. 函数重载 6.1 概念 6.2 为何C支持函数重载 7. 引用 7.1 概念 7.2 特性 7.3 常引用 7.4 引用与指针的区别 7…

docker-compose安装dozzle

dozzle是一个docker日志的webui工具 安装配置 docker-compose.yaml version: "3" services:dozzle:container_name: dozzleimage: amir20/dozzle:v4.11.4volumes:- /var/run/docker.sock:/var/run/docker.sockrestart: unless-stoppedports:- 20342:8080networks:cu…