文章目录

  • JWT 简介
    • 概念
    • JWT 的认证流程
    • 优缺点
  • JWT 消息构成
    • header
    • playload
    • signature
  • SpringBoot 集成 JWT 实战
    • maven 依赖
    • JwtUtil
    • JwtToken
    • JwtInterceptor
    • WebConfig
    • JwtController
    • GlobalExceptionHandler
    • SpringbootJwtApplication
    • application.yml
  • 测试

JWT 简介

概念

JWT全称是:json web token。它将用户信息加密到 token 里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证 token 的正确性,只要正确即通过验证。

JWT 的认证流程

1、用户输入用户名和密码,发送给服务器,服务器验证账号密码成功
2、服务器使用签名秘钥生成jwt,把用户id放到jwt中
3、把令牌返给客户端
4、下次请求的时候就把令牌放在请求头里带上
5、服务器使用签名秘钥验证jwt是否有效
6、有效后可以从jwt中获取到用户id

优缺点

优点
1、 简洁,可以通过 URL POST 参数或者在 HTTP header 发送,因为数据量小,传输速度也很快;
2、自包含,负载中可以包含用户所需要的信息,避免了多次查询数据库;
3、跨平台,因为 Token 是以 JSON 加密的形式保存在客户端的,所以 JWT 是跨语言的,原则上任何 web 形式都支持;
4、 存储在客户端,不需要再服务端保存会话信息,特别适用于分布式微服务;

缺点
1、无法作废已经发布的令牌
2、不易应对数据过期

JWT 消息构成

一个 Token 分三部分,按顺序为

1、头部(header)
2、载荷(payload)
3、签证(signature)

三个部分之间用.分割。例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIzOTZlNWZlNS0yZmQyLTQ3MjItYjQzOS0zYzY4NzA1OGMwZjAiLCJleHAiOjE2NjgxNzEyMTJ9.JUMFKdhzu1w_ecwHrOkqjKjosy3TOnaTrj1oFekG9HE

header

JWT的头部承载两部分信息:
1、声明类型,这里是JWT
2、声明加密的算法,通常直接使用 HMAC SHA256

JWT里验证和签名使用的算法列表如下:

playload

载荷就是存放有效信息的地方。基本上填两种类型的数据
1、标准中注册的声明的数据;
2、自定义数据;
由这两部分内部做 base64 加密。

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

自定义数据:存放我们想放在 token 中存放的 key-value 值。

signature

JWT的第三部分是一个签证信息,这个签证信息由三部分组成:
1、base64 加密后的 header
2、base64 加密后的 payload 连接组成的字符串
3、然后通过 header 中声明的加密方式进行加盐 secret 组合加密
然后就构成了JWT的第三部分。

SpringBoot 集成 JWT 实战

源代码地址:https://gitee.com/leo825/springboot-learning-parents.git

项目整体结构:

maven 依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.example</groupId><artifactId>springboot-learning-parents</artifactId><version>1.0-SNAPSHOT</version></parent><groupId>springboot-demo</groupId><artifactId>springboot-h2</artifactId><version>1.0-SNAPSHOT</version><name>springboot-jwt</name><url>https://gitee.com/leo825/springboot-learning-parents.git</url><description>springboot集成jwt测试</description><properties><start-class>com.demo.SpringbootJwtApplication</start-class><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><mybatisplus.version>3.5.1</mybatisplus.version><freemaker.version>2.3.31</freemaker.version><mysql.version>8.0.28</mysql.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- mybatis-plus 所需依赖  --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatisplus.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>${mybatisplus.version}</version></dependency><!-- 使用h2内存数据库 --><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId></dependency><!-- jwt依赖 --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.8.1</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

JwtUtil

package com.demo.util;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.auth0.jwt.interfaces.DecodedJWT;import java.util.Date;/*** jwt工具类*/
public class JwtUtil {/*** 过期时间5分钟*/private static final long EXPIRE_TIME = 5 * 60 * 1000;/*** jwt 密钥*/private static final String SECRET = "jwt_secret";/*** 生成签名,五分钟后过期** @param userId* @return*/public static String sign(String userId) {try {Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);Algorithm algorithm = Algorithm.HMAC256(SECRET);return JWT.create()// 将 userId 保存到 token 里面.withAudience(userId)// 五分钟后token过期.withExpiresAt(date)// token 的密钥.sign(algorithm);} catch (Exception e) {return null;}}/*** 根据 token 获取 userId** @param token* @return*/public static String getUserId(String token) {try {String userId = JWT.decode(token).getAudience().get(0);return userId;} catch (JWTDecodeException e) {return null;}}/*** 校验token** @param token* @return*/public static boolean checkSign(String token) {try {Algorithm algorithm = Algorithm.HMAC256(SECRET);JWTVerifier verifier = JWT.require(algorithm)// .withClaim("username", username).build();DecodedJWT jwt = verifier.verify(token);return true;} catch (JWTVerificationException exception) {throw new RuntimeException("token 无效,请重新获取");}}
}

JwtToken

package com.demo.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 加上该注解的接口需要登录才能访问*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JwtToken {boolean required() default true;
}

JwtInterceptor

package com.demo.interceptor;import com.demo.annotation.JwtToken;
import com.demo.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;/*** jwt拦截器*/
@Slf4j
public class JwtInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) {// 从 http 请求头中取出 tokenString token = httpServletRequest.getHeader("token");// 如果不是映射到方法直接通过if (!(object instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) object;Method method = handlerMethod.getMethod();//检查有没有需要用户权限的注解if (method.isAnnotationPresent(JwtToken.class)) {JwtToken jwtToken = method.getAnnotation(JwtToken.class);if (jwtToken.required()) {// 执行认证if (token == null) {throw new RuntimeException("无token,请重新登录");}// 获取 token 中的 userIdString userId = JwtUtil.getUserId(token);log.info("用户id: {}", userId);// 验证 tokenJwtUtil.checkSign(token);}}return true;}
}

WebConfig

package com.demo.config;import com.demo.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 注册拦截器*/
@Configuration
public class WebConfig implements WebMvcConfigurer {/*** 添加jwt拦截器** @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor())// 拦截所有请求,通过判断是否有 @JwtToken 注解 决定是否需要登录.addPathPatterns("/**");}/*** jwt拦截器** @return*/@Beanpublic JwtInterceptor jwtInterceptor() {return new JwtInterceptor();}
}

JwtController

package com.demo.controller;import com.alibaba.fastjson.JSONObject;
import com.demo.annotation.JwtToken;
import com.demo.util.JwtUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;
import java.util.UUID;@RestController
@RequestMapping("/jwt")
public class JwtController {/*** 登录并获取token** @param userName* @param passWord* @return*/@PostMapping("/login")public Object login(String userName, String passWord) {JSONObject jsonObject = new JSONObject();// 检验用户是否存在(为了简单,这里假设用户存在,并制造一个uuid假设为用户id)String userId = UUID.randomUUID().toString();// 生成签名String token = JwtUtil.sign(userId);Map<String, String> userInfo = new HashMap<>();userInfo.put("userId", userId);userInfo.put("userName", userName);userInfo.put("passWord", passWord);jsonObject.put("token", token);jsonObject.put("user", userInfo);return jsonObject;}/*** 该接口需要带签名才能访问** @return*/@JwtToken@GetMapping("/getMessage")public String getMessage() {return "你已通过验证";}
}

GlobalExceptionHandler

package com.demo.common;import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;/*** 全局异常处理*/
@RestControllerAdvice
public class GlobalExceptionHandler {@ResponseBody@ExceptionHandler(Exception.class)public Object handleException(Exception e) {String msg = e.getMessage();if (msg == null || msg.equals("")) {msg = "服务器出错";}JSONObject jsonObject = new JSONObject();jsonObject.put("code", 500);jsonObject.put("message", msg);return jsonObject;}
}

SpringbootJwtApplication

package com.demo;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan(value = "com.demo.mapper")
public class SpringbootJwtApplication {public static void main(String[] args) {SpringApplication.run(SpringbootJwtApplication.class, args);}
}

application.yml

#端口,项目上下文
server:port: 8080servlet:context-path: /springboot-jwtspring:datasource:username: leo825password: 1WSX@357wj# 如果需要数据本地化,则改成 file 方式# jdbc:h2:file:D:/program/sqlite3/db/testDB;AUTO_SERVER=TRUE;DB_CLOSE_DELAY=-1url: jdbc:h2:mem:testDB;DB_CLOSE_DELAY=-1driver-class-name: org.h2.Driver# 初始化表schema: classpath:schema.sql# 初始化数据data: classpath:data.sqlinitialization-mode: alwayscontinue-on-error: true# 开启这个配置就可以通过 web 页面访问了,例如:http://localhost:8080/springboot-h2/h2-consoleh2:console:enabled: truesettings:# 开启h2 console 跟踪 方便调试  默认 falsetrace: true# 允许console 远程访问 默认falseweb-allow-others: true# h2 访问路径上下文path: /h2-console# mybatis-plus 配置
mybatis-plus:mapper-locations: classpath*:/mapper/**/*.xml#实体扫描,多个package用逗号或者分号分隔typeAliasesPackage: com.dmo.entityglobal-config:#数据库相关配置db-config:#主键类型  AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";id-type: AUTO#字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"field-strategy: NOT_NULL#驼峰下划线转换column-underline: truelogic-delete-value: -1logic-not-delete-value: 0banner: false#原生配置configuration:# 打印sqllog-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: truecache-enabled: falsecall-setters-on-nulls: truejdbc-type-for-null: 'null'# 日志输出配置
logging:level:root: INFOorg:springframework:security: WARNweb: ERRORfile:path: ./logsname: './logs/springboot-jwt.log'pattern:file: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n'console: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n'

测试

1、登录

2、验证jwt

3、验证jwt(无token失败)

SpringBoot笔记:SpringBoot集成JWT实战相关推荐

  1. SpringBoot脚手架工程集成jwt

    https://gitee.com/niugangxy/sprigboot-new-coding-standards/tree/master/new-coding-standards-jwt     ...

  2. SpringBoot 快速集成 JWT 实现用户登录认证

    前言:当今前后端分离时代,基于Token的会话保持机制比传统的Session/Cookie机制更加方便,下面我会介绍SpringBoot快速集成JWT库java-jwt以完成用户登录认证. 一.JWT ...

  3. springboot 集成jwt设置过期时间_传说中的jwt,我们来征服一下

    原创:猿逻辑,欢迎分享,转载请保留出处. 本文的完整示例代码,见github仓库.小q只在文中介绍最关键的代码块. https://github.com/yuanluoji/purestart-spr ...

  4. SpringBoot笔记:SpringBoot2.3集成SpringSession+nginx+redis实现session共享

    文章目录 Spring Session介绍 Redis集成 yml配置 依赖添加 redis存值查看 登录服务器查看redis的值 查询所有"spring:session:"开头的 ...

  5. springboot entity date_SpringBoot+JWT实战(附源码)

    SpringBoot集成JWT 首先我们搭建好SpringBoot框架,SpringBoot环境准备就绪.接下来执行以下操作: 1.引入依赖 引入JWT依赖,由于是基于Java,所以需要的是java- ...

  6. SpringBoot集成JWT实现Token登录验证

    目录 1.1 JWT是什么? 1.2 JWT主要使用场景 1.3 JWT请求流程 1.4 JWT结构 二,SpringBoot集成JWT具体实现过程 2.1添加相关依赖 2.2自定义跳出拦截器的注解 ...

  7. JWT springboot集成jWT

    1.1官网介绍 地址:https://jwt.io/ JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compac ...

  8. Springboot集成JWT做认证授权

    目录 1.1.JWT简介 1.2.JWT能做什么? 1.3.JWT认证流程是什么? 1.4.JWT的结构是什么? 1.5.使用JWT 1.生成token 2.解析token 1.6.封装工具类 1.7 ...

  9. SpringBoot集成JWT实现token验证

    Jwt全称是:json web token,以JSON对象的形式安全的传递信息.它将用户信息加密到token里,服务器不保存任何用户信息.服务器通过使用保存的密钥验证token的正确性,只要正确即通过 ...

最新文章

  1. Python、Matplot的subplot实现一行3列的子图绘制,并添加背景色
  2. 真良心大厂EPIC,页游广告又有新素材了!
  3. 光模块、连接器、光纤的常用知识
  4. 当今将Windows应用程序迁移到Windows on Arm的实践
  5. Connectivity Matrices(连通矩阵)
  6. Ubuntu安装报错E: Could not get lock /var/lib/dpkg/lock-frontend
  7. 【CC2640R2F】香瓜CC2640R2F之LED
  8. cas服务器源码,Cas服务端源码解析
  9. 窄带包络解调python实现_对数据包络分析法DEA的再理解,以及python 实现
  10. ExtJS6-项目创建
  11. Comsol Multiphysics安装步骤详解
  12. 墓碑上的字符C语言,墓碑上常见的“故显考、故显妣、先考、先妣”,分别是什么意思?...
  13. 训练趣题:黑与白 有A、B、C、D、E五人,每人额头上都帖了一张黑或白的纸。(此处用javascript实现)...
  14. 【数据分析】数据分析方法(一):5W2H 分析方法
  15. 做外贸必备的十大网站
  16. aptx android8,支持aptxHD和LDAC!安卓8.0蓝牙音质大爆发
  17. 初羡微商代理发货系统授权系统开发
  18. python调用包的路径_Python3 模块、包调用路径详解
  19. python爬取虎扑论坛帖子数据
  20. c# 火狐浏览器怎么嵌入窗体中_正在学c# winform,问各位前辈一下 能调用火狐或者其他的浏览器吗?...

热门文章

  1. Android设置文字中粗
  2. 微信收付通分账额度超过30%怎么办
  3. 磁盘、柱面、磁道、磁头、扇区
  4. 湖北省两化融合贯标申请认定条件、流程、奖补标准细则
  5. 做IAP远程升级时,APP程序地址修改了中断向量偏移地址不起效果的原因分析
  6. 5天不再惧怕多线程——第一天 尝试Thread
  7. war包方式部署solo博客
  8. 十进制转二进制的笔算过程
  9. ES6 for..in 和 for...of 和 for循环
  10. OSChina 周三乱弹 ——Java酱接过了最后一棒