0


JWT 工作原理及其应用 从0~0.5 快速整合SpringBoot以及Mybatis 二刷绝对适合你~

📑本篇内容:JWT 工作原理及其应用 从0~0.5 快速整合SpringBoot以及Mybatis 二刷绝对适合你!

📘 文章专栏:前端知识(后端需掌握知识点)

前后端分离项目(Vue + SpringBoot)

🎬最近更新:2022年2月2日 Vue中的路由 Router 从0 ~ 0.5 基础通晓到使用 (后端人员需要掌握的基础使用)

🙊个人简介:一只二本院校在读的大三程序猿,本着注重基础,打卡算法,分享技术作为个人的经验总结性的博文博主,虽然可能有时会犯懒,但是还是会坚持下去的,如果你很喜欢博文的话,建议看下面一行~(疯狂暗示QwQ)

🌇点赞 👍 收藏 ⭐留言 📝 一键三连 关爱程序猿,从你我做起

📖本文目录

📑JWT 原理及应用

🚀1、 JWT的简介

🎁1、什么是JWT?

什么是 JWT ?

JWT(全称

JSON Web Tokens

)我们通常称之为

JSON Web 令牌

,它是个十分流行的

跨域认证的一种解决方案

🎁2、Token又是什么?

Token 又是什么呢?

Token是

服务端生成的一串字符串

,以作

客户端进行请求的

一个

令牌

,当第

一次登录后

,服务器生成一个Token便

将此Token返回给客户端

,以后

客户端只需带上这个Token

前来

请求数据即可

无需

再次

带上用户名和密码

使用Token的目的:Token的目的是

为了减轻服务器的压力

减少

频繁的

查询数据库

使服务器更加健壮


**

JWT

实际上

是 token 的一种具体实现方式

。**


🎁3、概念

官方参考文献:

JSON Web Tokens - jwt.io

官方给出的概念

Json web token(JWT)

是为了网络应用环境间传递声明而执行的一种

基于JSON

的开发标准(

RFC 7519

),

用于对双方之间以JSON对象安全的传输信息

在这里插入图片描述

简单的来说:对于学过信息安全技术的同学就知道,

为保证了数据的发送方不可抵赖

,以及

数据的接收方能够安全并且进行验证获取信息

,通常都是要

对数据进行加密

签名等

相关操作。

JWT

就是用于在各个用户之间通过

JSON对象数据

进行的

安全传输

的令牌。

再简单点理解就是:我们把

用户提出请求中的数据

保存到

一个JSON字符串

中,然后

通过某些算法

进行

编码

得到

了一个

JWT

,此时这个 JWT

已经被加密签名

这些操作了, 然后服务器

对其进行接收

,进行

验证签名

,并且对之

解密获取用户提交的数据

🚀2、JWT的工作流程

在这里插入图片描述

其工作流程一般分为如下6步

  • 一开始,当用户在前端通过表单提交自身的数据发送到后端对应的API接口时,建议采用加密传输协议,防止信息泄露。
  • 后端对用户提交的数据进行验证,认证成功后,将Token的类型和使用的算法作为Header通过Base64进行编码生成 JWT 的第一部分结构xxxxx,将含有用户信息的数据作为Payload(负载)进行Base64的编译生成 JWT 的第二部分结构,得到的编码作为yyyyy,随后与签名作为 JWT 的第三部分结构 (zzzzz)进行拼接,形成一个JWT Token的字符串:xxxxx,yyyyy,zzzzz
  • 后端将 JWT Token字符串 作为用户认证成功后的请求响应返回给前端。前端可以将返回的结果保存在浏览器当中,退出登录后会删除所对应的 JWT Token
  • 前端会在每次请求时,将上面后端传给浏览器的JWT Token 一并放到HTTP请求头中的 **Authorization**属性 (解决恶意跨域、跨站请求访问的问题)。
  • 后端的拦截请求器会验证 JWT Token有效性签名正确性以及是否过期等验证操作。
  • JWT Token验证通过后,后端解析出 JWT Token中的用户信息,返回结果。

注意:因为Base64是开放加密算法,所以一定不要在负载中存放敏感信息(用户密码等…)

🚀3、JWT的结构组成

在这里插入图片描述

🚀4、Hello JWT

步骤1:创建一个SpringBoot项目,整合 JWT 相关依赖

pom.xml

<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency>

步骤2:编写一个 JWT 工具类用于生成令牌

JWTUtils.java

/**
 * 功能描述
 * JWT的工具类
 * @author Alascanfu
 * @date 2022/2/3
 */publicclassJWTUtils{/**
     * 功能描述
     * 生成令牌
     * @date 2022/2/3
     * @author Alascanfu
     */publicstaticStringcreateToken(){Calendar instance =Calendar.getInstance();
        instance.add(Calendar.SECOND,180);String token = JWT.create()//生成令牌.withClaim("username","Alascanfu")//设置payload.withExpiresAt(instance.getTime())//设置过期时间.sign(Algorithm.HMAC256("token!Q#28$5RCCF"));//设置signature return token;}/**
     *功能描述
     * 令牌验证
     * @date 2022/2/3
     *  @author Alascanfu
     */publicstaticDecodedJWTrequireToken(String token){JWTVerifier jwtVerifier = JWT
            .require(Algorithm.HMAC256("token!Q#28$5RCCF")).build();return jwtVerifier.verify(token);}}

步骤3:创建一个测试类进行测试

JWTJunitTest.java

@SpringBootTestpublicclassJWTJunitTest{@Testpublicvoidtest(){//创建一个tokenString token =JWTUtils.createToken();System.out.println("Token :"+ token);//拿着获取的token得到decodedJWTDecodedJWT decodedJWT =JWTUtils.requireToken(token);//通过decodedJWT获取数据Claim username = decodedJWT.getClaim("username");System.out.println(username);String username = decodedJWT.getClaim("username").asString();System.out.println(username);}}

执行结果:

返回的Token令牌:

Token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NDM4NTk5MDQsInVzZXJuYW1lIjoiQWxhc2NhbmZ1In0.HYPycutSIfgfh_vNFL6fp2P3mzlBiAmuH7vAy0D-64k
com.auth0.jwt.impl.JsonNodeClaim@5bc7e78e
Alascanfu

⭐5、JWT 中常见异常讲解⭐

在这里插入图片描述

我们可以根据jwt的依赖包找到对应的exceptions

  • SignatureVerificationException: 签名认证错误异常
  • TokenExpiredException: 令牌过期错误异常。
  • AlgorithmMismatchException:校验算法不匹配的错误异常。
  • InvalidClaimException:失效负载数据异常。

⭐6、JWT 工具封装⭐

JWTUtil.java

/**
 * 功能描述
 * JWT封装工具类
 * @author Alascanfu
 * @date 2022/2/3
 */publicclassJWTUtil{privatestaticString TOKEN ="&$s78"+UUID.randomUUID().toString()+"#34%6";/**
     * 功能描述
     * 生成Token令牌
     * @date 2022/2/3
     * @author Alascanfu
     */publicstaticStringgetToken(Map<String,String> map){JWTCreator.Builder builder = JWT.create();//向其中添加负载数据//23种设计模式中典型的建造者模式
        map.forEach((key,val)->{
            builder.withClaim(key,val);});//设置过期时间Calendar instance =Calendar.getInstance();
        instance.add(Calendar.SECOND,90);
        builder.withExpiresAt(instance.getTime());//进行签名拼接return builder.sign(Algorithm.HMAC256(TOKEN)).toString();}/**
     * 功能描述
     * 验证Token令牌的合法性
     * @date 2022/2/3
     * @author Alascanfu
     */publicstaticvoidverify(String token){
        JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token);}/**
     * 功能描述
     * 获取DecodedJWT 用于获取负载数据
     * @date 2022/2/3
     * @author Alascanfu
     */publicstaticDecodedJWTgetToken(String token){return  JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token);}}

用于测试封装类的测试类

JWTJunitTest.java

@SpringBootTestpublicclassJWTJunitTest{@Testpublicvoidtest(){HashMap<String,String> map =newHashMap<>();
        map.put("username","Alascanfu");
        map.put("id","201901094106");String token =JWTUtil.getToken(map);System.out.println(token);JWTUtil.verify(token);DecodedJWT token1 =JWTUtil.getToken(token);System.out.println(token1.getClaim("username").asString());System.out.println(Long.parseLong(token1.getClaim("id").asString()));}}

⭐7、整合SpringBoot+Mybatis的简单案例⭐

步骤1:创建一个数据库并且生成一张user表填入部分数据

CREATEDATABASE jwt;CREATETABLEuser(`id`intnotnullprimarykeyauto_increment,`username`VARCHAR(64)notnull,`password`VARCHAR(64)notnull)engine=innodbdefaultcharset=utf8;INSERTINTO`jwt`.`user`(`id`,`username`,`password`)VALUES(1,'Alascanfu','123456');

在这里插入图片描述

步骤2:创建SpringBoot项目导入相关所需依赖及部分配置

pom.xml

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency>

配置好application.yaml

spring:datasource:username: root
    password:123456url: jdbc:mysql://localhost:3306/jwt?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    druid:#Spring Boot默认是不注入这些属性值的,需要自我绑定#druid 数据源专有配置initialSize:5minIdle:5maxActive:20# 配置获取连接等待超时的时间maxWait:60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis:60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis:300000validationQuery: SELECT 1 FROM DUAL
      testWhileIdle:truetestOnBorrow:falsetestOnReturn:false# 打开PSCache,并且指定每个连接上PSCache的大小poolPreparedStatements:truemaxPoolPreparedStatementPerConnectionSize:20# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙,此处是filter修改的地方filters: stat,wall,log4j
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      # 合并多个DruidDataSource的监控数据useGlobalDataSourceStat:true

配置好log4j.properties

log4j.rootLogger=DEBUG, Console,file
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n

#文件输出的相关配置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./log/DataLog.log
log4j.appender.file.MaxFileSize = 10mb
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = [%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

步骤3:编写用户类及其Mapper、service、controller等实例数据业务

User.java

publicclassUser{privateint id ;privateString username;privateString password;publicUser(){}publicUser(String username,String password){this.username = username;this.password = password;}//...get/set方法@OverridepublicStringtoString(){return"User{"+"id="+ id +", username='"+ username +'\''+", password='"+ password +'\''+'}';}}

UserMapper.java

@Mapper@RepositorypublicinterfaceUserMapper{/**
     * 功能描述
     * 通过用户名和密码校验用户进行查询信息
     * @date 2022/2/3
     * @author Alascanfu
     */publicUserqueryUser(User user);}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.alascanfu.mapper.UserMapper"><cachereadOnly="false"flushInterval="60000"size="512"eviction="LRU"/><selectid="queryUser"resultType="com.alascanfu.pojo.User">
        select id, username, password from user where user.username = #{username} and user.password = #{password}
    </select></mapper>

UserService.java

@ServicepublicclassUserService{@AutowiredUserMapper userMapper;@Transactional(propagation =Propagation.REQUIRED)publicUserqueryUser(User user){if(userMapper.queryUser(user)!=null){return userMapper.queryUser(user);}else{thrownewRuntimeException("用户名或密码错误~");}}}

注意:此时进行单元测试检阅是否能获得得到数据库中的数据

HelloJwtApplicationTests.java

@SpringBootTestclassHelloJwtApplicationTests{@AutowiredUserService userService;@TestvoidcontextLoads(){System.out.println(userService.queryUser(newUser("Alascanfu","123456")).toString());}}

如果出现了绑定错误记得要去pom.xml中进行配置yaml、xml、properties等文件也要被打包到target中哦~标签中进行修改。

<resource><directory>src/main/java</directory><includes><include>**/*.xml</include><include>**/*.yaml</include><include>**/*.yml</include><include>**/*.properties</include></includes><filtering>true</filtering></resource><resource><directory>src/main/resources</directory><includes><include>**/*.xml</include><include>**/*.yaml</include><include>**/*.yml</include><include>**/*.properties</include></includes><filtering>true</filtering></resource>

步骤4:编写Controller

UserController.java

@RestControllerpublicclassUserController{@AutowiredUserService userService;@RequestMapping("/user/login")publicMap<String,Object>login(User user){Map<String,Object> map =newHashMap<>();try{User queryUser = userService.queryUser(user);Map<String,String> payload =newHashMap<>();
            payload.put("id",String.valueOf(queryUser.getId()));
            payload.put("username",queryUser.getUsername());String token =JWTUtil.getToken(payload);
            map.put("status",true);
            map.put("msg","认证成功!");
            map.put("token",token);}catch(Exception e){
            map.put("status",false);
            map.put("msg","认证失败!");}return map;}}
进行测试
  • 可见用户登录成功后,我们将JWT工具类生成的token也一并返回给浏览器了。

在这里插入图片描述

  • 当我们需要请求需要保护的业务接口时,可以验证其接口后再进行业务处理,或者之前用户登录过后不想再次登录时,都可以基于token免除登录,直接访问。

额外接口

@RequestMapping("/user/index")publicMap<String,Object>userIndex(String token){System.out.println(token);Map<String,Object> map =newHashMap<>();try{JWTUtil.verify(token);DecodedJWT verify =JWTUtil.getToken(token);
            map.put("state",true);
            map.put("msg","请求成功!");return map;}catch(Exception e){
            e.printStackTrace();
            map.put("msg",e.toString());}
        map.put("state",false);return map;}

接口测试

  • 验证token成功所显示的数据

在这里插入图片描述

  • 验证token失败所显示

在这里插入图片描述

注意

:如果给每个业务API编写上述会使得代码冗余,为了减少代码冗余,我们可以将这个公共的操作放在单体JavaSpringBoot应用中的拦截器中去做,如果是SpringCloud分布式服务模块咱们就可以去网关进行配置。

编写拦截器

interceptors/JWTInterceptor.java

publicclassJWTInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{String token = request.getHeader("token");Map<String,Object> map =newHashMap<>();try{//验证JWTUtil.verify(token);//如果能验证成功直接放行returntrue;}catch(TokenExpiredException e){
            map.put("msg","Token已经过期~");}catch(SignatureVerificationException e){
            map.put("msg","签名错误~");}catch(AlgorithmMismatchException e){
            map.put("msg","加密算法不匹配~");}catch(Exception e){
            e.printStackTrace();
            
            map.put("msg","无效token~");}
        map.put("state",false);//通过jackson转化map为json数据String json =newObjectMapper().writeValueAsString(map);
        
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);returnfalse;}}

将我们的拦截器注入到Spring容器当中,我们需要写一个配置类继承WebMvcConfigurer对SpringMVC进行扩展

config/intercceptorConfig.java

@ConfigurationpublicclassIntercsptorConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(newJWTInterceptor()).addPathPatterns("/user/**")//需要拦截的路径.excludePathPatterns("/user/login");//但是生成token的路径不能拦截}}

进行测试验证

POSTMAN测试

在这里插入图片描述

  • 当我们没有任何token携带的时候前端页面返回的是无效token异常。
  • 后端显示的是前端控制器DispatcherServlet拦截器爆出的异常错误

在这里插入图片描述

  • 然后我们对其进行登录先生成一个token。

在这里插入图片描述
在这里插入图片描述

  • 随后我们获得了后端发给我们的token数据。将其添加到请求头中。

在这里插入图片描述

  • 此时我们再去访问需要验证token的部分API接口时就无须很复杂处的操作了,拦截器都帮我们执行好了。

在这里插入图片描述

🙊总结

本次整理的是前后端分离项目或者单体SpringBoot应用中,基于JWT令牌认证的知识总结,可以

有效避免了使用之前较为复杂的Session校验

,同时

安全性便捷性都也有一定的提高

,对于

每个业务API都有token认证

,可以为我们

省去开发中很多问题

,同时后续结合SpringSecurity可以让系统更加的安全可靠,大概就是这么多了,

大概一两个小时就可以入门到使用

,也了解

其中的工作原理

,这就是

后端人员需要掌握的JWT的知识咯

~

在这里插入图片描述

标签: spring boot vue.js jwt

本文转载自: https://blog.csdn.net/fuijiawei/article/details/122776957
版权归原作者 猿小付 所有, 如有侵权,请联系我们删除。

“JWT 工作原理及其应用 从0~0.5 快速整合SpringBoot以及Mybatis 二刷绝对适合你~”的评论:

还没有评论