Springboot整合JWT
1. 应用场景
前后端分离项目保持登录状态。
问题:ajax请求如何跨域,将无法携带jsessionid,这样会导致服务器端的session不可用。如何解决?
后端:
登录接口在验证过用户密码后,将用户的身份信息转换成一个特殊格式的字符串(token),放入响应体(或响应头)。
前端:
浏览器收到登录成功响应后,保存该token在本地,当下次发送请求时,取出该token并携带在请求头(或请求体中)。
后端:
使用filter或者拦截器校验请求中的token是否有效,如果有效就代表用户已登录,放行。
2. 对于token的需求
(1) 可以自定义信息(用户身份、权限信息)
(2) 需要使得服务器端可以进行有效性校验(即token只能由服务器端颁发,不能由其他第三方伪造)
(3) 需要能设置有效期
3. JWT的数据结构
https://jwt.io/ 官网地址
(1)HEADER //头
(2)PAYLOAD //体
(3)SIGNATURE //签名
4. 生成JWT
HMACSHA256(
base64UrlEncode(header) + "." +base64UrlEncode(payload),
your-256-bit-secret
)
(1)计算header的base64编码
(2)计算payload的base64编码
(3)计算签名
5. 校验token是否合法
原理:重新计算token的sign,与token中的sign进行比对,一致则合法。
6. 校验token有效期
原理,在jwt的payload中加入过期日期,然后在校验token时检查是否过期
这是一个测试类需要导入Hutool工具类
package com.qf.fmall.jwt;import cn.hutool.jwt.JWT;import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;public class TestJwt {public static final String secretkey="qfsyjava2302";public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException {String token1 = createToken();//验证token的合法性
// String token="eyJoZWFkZXIwMSI6InRlc3QxMjMiLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsdWZmeSIsImlhdCI6MTY5MzIwNjc2NiwicGhvbmUiOiIxMzg5OTk5ODg4OCIsInNleCI6MSwiYWdlIjoxOX0.UY3v3twt30GAN6iCOlTTZwA0YpwMBFDg3JyWgxp1_7U";Thread.sleep(3000);//使用hutool工具类校验Jwtboolean validate = JWT.of(token1)//把token字符串传入,用来构建jwt对象.setKey(secretkey.getBytes("utf-8"))//传入计算签名要使用的密钥
// .verify() 只验证token值,不验证时间.validate(3l);//校验jwt是否合法,本质上就是重算签名,并且比较签名是否一致。//这里的leeway是秒,就是在原本的设定的失效时间,容许容忍几秒System.out.println(validate);}public static String createToken() throws UnsupportedEncodingException {//利用hutool工具类生成JwtHashMap<String, Object> payloadMap = new HashMap<>();payloadMap.put("age",19);payloadMap.put("sex",1);payloadMap.put("phone","13899998888");HashMap<String, Object> headMap = new HashMap<>();headMap.put("header01","test123");String token = JWT.create().addHeaders(headMap) //放入自定义Jwt 头.setSubject("luffy")//在 jwt 的payload中放入 sub(代表用户身份信息).setExpiresAt(new Date(System.currentTimeMillis()+1000*3))// 在jwt的payload中放入 jwt 失效时间.setIssuedAt(new Date()) //在 jwt 的payload 中 放入 jwt的签发时间.addPayloads(payloadMap) // 在jwt 的payload 中放入其他自定义内容.setKey(secretkey.getBytes("utf-8")) //放入计算jwt 需要的密钥字符串.sign();System.out.println(token);return token;
}}
7. 开启shiro登录校验
1.用拦截器的方式实现
实现拦截器接口
package com.qf.fmall.interceptor;import cn.hutool.jwt.JWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
@Slf4j
public class JwtInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//0.判断如果是浏览器发送的Option 请求,直接放行String method = request.getMethod();if (method.equals("OPTIONS")){return true;}//1.获取 请求头中的 tokenString token= request.getHeader("token");//校验jwttokenif (token==null){log.info("没带token,拒绝访问");return false;}boolean validate = JWT.of(token).setKey("qfsyjava2302".getBytes("utf-8")).validate(0);return validate;}
}
注册拦截器
package com.qf.fmall.config;import com.qf.fmall.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
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 IntercepterConfig implements WebMvcConfigurer {@AutowiredJwtInterceptor jwtInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor).addPathPatterns("/index/indeximg");}
}
2.整合shiro过滤器
1.前端登录方法
@GetMapping("/login")@CrossOriginpublic ResultVo login(@RequestParam("username") String username,@RequestParam("password") String password) throws JsonProcessingException {Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token =new UsernamePasswordToken(username,password);try {subject.login(token);Users principal = (Users) subject.getPrincipal();//当用户登录成功之后,创建jwt//把principal---> jsonObjectMapper mapper = new ObjectMapper();//获取springboot内置的json转换对象String userjson = mapper.writeValueAsString(principal);String jwt = JWT.create().setSubject(userjson).setExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 30)).setIssuedAt(new Date()).setKey("qfsyjava2302".getBytes()).sign();return ResultVo.vo(10000,jwt,principal);} catch (AuthenticationException e) {e.printStackTrace();return ResultVo.vo(112300,"failed",null);}}
2.后端验证过滤器
package com.qf.fmall.filter;import cn.hutool.jwt.JWT;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.stereotype.Component;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;@Component("jwt")
@Slf4j
public class JwtFilter extends AccessControlFilter {/*** 用于验证访问受保护的方法* @param servletRequest* @param servletResponse* @param o* @return* @throws Exception*/@Overrideprotected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {HttpServletRequest request = (HttpServletRequest) servletRequest;//0.判断如果是浏览器发送的Option 请求,直接放行String method = request.getMethod();if (method.equals("OPTIONS")){return true;}//1.获取 请求头中的 tokenString token= request.getHeader("token");//校验jwttokenif (token==null){log.info("没带token,拒绝访问");return false;}boolean validate = JWT.of(token).setKey("qfsyjava2302".getBytes("utf-8")).validate(0);return validate;}/*** onAccessDenied 方法是Shiro框架中的一个回调方法,当主体(用户)被拒绝访问特定资源时会被调用。该方法不返回布尔值。* 当主体尝试访问受保护的资源但权限不足或未认证时,Shiro会触发 onAccessDenied 方法。通常,它用于处理被拒绝访问的情况,例如将用户重定向到错误页面或返回错误消息。* 因此,说 onAccessDenied 方法返回 true 是没有意义的。该方法的目的是处理访问被拒绝的情况,而不是返回布尔值。* @param servletRequest* @param servletResponse* @return* @throws Exception*/@Overrideprotected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {return false;}
}
3.让请求走shiro过滤器
在ShiroConfig类下写
//配置Shiro过滤器链@Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition(){DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();chainDefinition.addPathDefinition("/user/login**","anon");chainDefinition.addPathDefinition("/user/regist**","anon");chainDefinition.addPathDefinition("/error","anon");//让/index/indeximg 由自定义的shiro filter 进行处理, "jwt" 这个值是IOC容器中filter的名字chainDefinition.addPathDefinition("/index/indeximg","jwt");
// chainDefinition.addPathDefinition("/**","authc");return chainDefinition;}
但是shiro不仅会在shiro过滤器链下注册此过滤器,还会在springmvc过滤器链上注册
所以我们,还要让springmvc上的过滤器链上的此过滤器失效
4.让全局过滤器链上的JwtFilter失效
package com.qf.fmall.config;import com.qf.fmall.filter.JwtFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 注意,为了不让 自定义filter也被中注册到全局FitlterChain中,* 需要添加如下配置类*/
@Configuration
public class FilterConfig {@AutowiredJwtFilter jwtFilter;@Beanpublic FilterRegistrationBean<JwtFilter> jwtFilterFilterRegistrationBean(){FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();//要注册的filter的对象registrationBean.setFilter(jwtFilter);//让当前filter不用注册到,全局过滤器链上registrationBean.setEnabled(false);return registrationBean;}}
相关文章:
Springboot整合JWT
1. 应用场景 前后端分离项目保持登录状态。 问题:ajax请求如何跨域,将无法携带jsessionid,这样会导致服务器端的session不可用。如何解决? 后端: 登录接口在验证过用户密码后,将用户的身份信息转换成…...
如何使用Python和正则表达式处理XML表单数据
在日常的Web开发中,处理表单数据是一个常见的任务。而XML是一种常用的数据格式,用于在不同的系统之间传递和存储数据。本文通过阐述一个技术问题并给出解答的方式,介绍如何使用Python和正则表达式处理XML表单数据。我们将探讨整体设计、编写思…...
LA@方阵相似@相似矩阵的性质
文章目录 相似矩阵引言相似矩阵定义相似变换相似变换矩阵相似矩阵的矩阵多项式和特征值相同推论:与对角阵相似的矩阵性质定理 相似矩阵性质相似矩阵的乘方性质相似矩阵和矩阵多项式相似对角阵 对角阵多项式的展开小结 相似矩阵 引言 对角阵是矩阵中最简单的一类矩阵 对角阵相…...
ZLMediaKit 各种推拉流
1 用ffmpeg 推音视频流 ./ffmpeg -f dshow -i video"HP Wide Vision HD Camera" -f dshow -i audio"麦克风阵列 (Realtek High Definition Audio)" -rtbufsize 100M -max_delay 100 -pix_fmt yuv420p -tune zerolatency -c:v libx264 -crf 18 -s 1280x720…...
行业追踪,2023-08-29
自动复盘 2023-08-29 凡所有相,皆是虚妄。若见诸相非相,即见如来。 k 线图是最好的老师,每天持续发布板块的rps排名,追踪板块,板块来开仓,板块去清仓,丢弃自以为是的想法,板块去留让…...
【简单】228. 汇总区间
原题链接:https://leetcode.cn/problems/summary-ranges/description/ 228. 汇总区间 给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖&…...
Mysql高级语句
高级语句 1.按关键字排序 SELECT column1, column2, ... FROM table_name ORDER BY column1, column2, ... ASC|DESC ASC 是按照升序进行排序的,是默认的排序方式,即 ASC 可以省略。 SELECT 语句中如果没有指定具体的排序方式,则默认按 ASC…...
Python中 re.compile 函数的使用
前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 以下介绍在python的re模块中怎样应用正则表达式 👇 👇 👇 更多精彩机密、教程,尽在下方,赶紧点击了解吧~ python源码、视频教程、插件安装教程、资料我都准备…...
【分布式搜索引擎es】
文章目录 数据搜索DSL实现查询文档搜索结果处理 RestClient实现 elasticsearch最擅长的是 搜索和 数据分析。 数据搜索 DSL实现 查询文档 常见的查询类型包括: 查询所有:查询出所有数据,一般测试用。例如:match_all全文检索…...
单片机的ADC
如何理解ADC。ADC就是将模拟量转换成数字量的过程,就是转换为计算机所能存储的0和1序列,比如将模拟量转换为一个字节,所以这个字节的大小要能反应模拟量的大小,比如一个0-5V的电压测量量(外部输入电压最小0V,最大为5V&…...
如何把pdf文件合并?分享最新pdf合并方法
在所有文档格式中,pdf应该是最常用的,像产品介绍、商务合同、法律文书等等,这些都是pdf格式的。有时候出于工作需要,我们要把两份或者多份pdf文件合并在一起,那么问题来了,如何把pdf文件合并呢?小编最近发…...
笙默考试管理系统-MyExamTest----codemirror(11)
笙默考试管理系统-MyExamTest----codemirror(11) 目录 笙默考试管理系统-MyExamTest----codemirror(11) 一、 笙默考试管理系统-MyExamTest----codemirror 二、 笙默考试管理系统-MyExamTest----codemirror 三、 笙默考试管…...
Spring MVC 五 - Spring MVC的配置和DispatcherServlet初始化过程
今天的内容是SpringMVC的初始化过程,其实也就是DispatcherServilet的初始化过程。 Special Bean Types DispatcherServlet委托如下一些特殊的bean来处理请求、并渲染正确的返回。这些特殊的bean是Spring MVC框架管理的bean、按照Spring框架的约定处理相关请求&…...
Ramp 有点意思的题目
粗一看都不知道这个要干什么,这 B 装得不错。 IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoKJycnCktlZXAgdXMgb3V0IG9mIGdvb2dsZSBzZWFyY2ggcmVzdWx0cy4uCgokIG9kIC1kIC9kZXYvdXJhbmRvbSB8IGhlYWQKMDAwMDAwMCAgICAgNjAyMTUgICAyODc3OCAgIDI5MjI3ICAgMjg1NDggICA2MjY4NiAgIDQ1MT…...
算法通关村14关 | 堆在数组中找第k大的元素应用
1. 在数组中找第k大元素 题目 LeetCode215:给定整数数组nums和整数k,请返回数组中第k个最大的元素, 思路 解题思路用三个,选择法,堆查找和快速排序。 我们选择用大堆小堆解决问题,“找最大用小堆ÿ…...
Unity 顶点vertices,uv,与图片贴图,与mesh
mesh就是组成3d物体的三角形们。 mesh由顶点组成的三角形组成,三角形的大小 并不 需要一样,由顶点之间的位置决定。 mesh可以是一个或者多个面。 贴图的原点在左下角,uv是贴图的坐标,数量和顶点数一样(不是100%确定…...
Shell编程之函数
目录 基本概念 自定义函数 系统函数 1.read 2.basename 3.dirname 基本概念 将一段代码组合封装在一起实现某个特定的功能或返回某个特定的值,然后给这段代码取个名字,也就是函数名,在需要实现某个特定功能的时候直接调用函数名即可。 函…...
10.物联网LWIP之TCP状态转变
一。TCP状态机 1.青粗线:理想TCP状态转变(服务器视角下) 2.虚线:被动TCP状态转变(服务器视角下) 3.细实线:不经常出现的TCP状态转变(类似于边界处理) 1.青粗线解释--》服…...
Img标签的src地址自动拼接本地域名(localhost:8080)导致图片不显示问题
摘要:做Vueelement ui项目的时候,发现使用element ui的upload上传图片时,不显示的问题。我项目的图片是上传到七牛云,长传成功后返回存储在七牛云中的地址。后面发现是因为返回的地址是外部地址,需要完整的URL…...
数据结构入门 — 栈
本文属于数据结构专栏文章,适合数据结构入门者学习,涵盖数据结构基础的知识和内容体系,文章在介绍数据结构时会配合上动图演示,方便初学者在学习数据结构时理解和学习,了解数据结构系列专栏点击下方链接。 博客主页&am…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
