05 SS之结合SS+Redis+JWT实现认证

之前的一切的有一个比较显著的问题是: 假如用户主动登出, 这意味着系统应将用户的token作废, 但实际上并未实现这一步, 导致了即使用户登出, 只要token在有效期内, 用户仍能凭借token直接进入.

即token处于一个无法销毁的状态.

可以借助Redis.

核心步骤是

① 第一次登陆成功之后不仅仅是返回JWT给用户,
而是将JWT在返回给用户的同时存到redis中

key value
logintoken:jwt UsernamePasswordAuthenticationToken

② 用户退出时,从redis中删除该token

③ 第二次以后用户每次访问时,先校验jwt是否合法,如果合法再从redis里面取出logintoken:jwt并判断这个jwt还存不存在,如果不存在就说是用户已经退出来,就返回未登陆。

1.2 修改认证(登陆)成功处理器,完成步骤1

1.2.1 加入依赖, 配置文件中增加redis配置

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency># 配置redis信息
spring:redis:host: 192.168.43.33port: 6379database: 0password: 666666

1.2.2 修改认证(登陆成功)处理器 , 使其能将JWT等信息放到Redis中

第一次认证(登录)成功后将JWT等信息存好

@Component
public class MyAuthenticationSuccessHandle implements AuthenticationSuccessHandler {//注入一个序列化器, 可以将JSON序列化, 反序列化@Resourceprivate ObjectMapper objectMapper;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {//省略代码,即根据authentication获取用户信息,用户权限信息, 并根据这些信息生成JWT/**第一第二个参数分别是key和value,我们这里选择将JWT填入key中,认证信息填入value中*objectMapper调用方法将authentication序列化为字符串填入value中*第三第四个参数分别是过期时间和时间单位*/stringRedisTemplate.opsForValue().set("logintoken:"+token,objectMapper.writeValueAsString(authentication),30, TimeUnit.MINUTES);}@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {response.setCharacterEncoding("UTF-8");response.setContentType("application/json;charset=utf-8");//借助Lombok实现建造者模式, 并通过建造者模式创建对象HttpResult httpResult = HttpResult.builder().code(1).msg("登陆成功").build();//将对象转化为JSONString responseJson = objectMapper.writeValueAsString(httpResult);PrintWriter writer = response.getWriter();writer.println(responseJson);writer.flush();}}

1.3 修改登出成功处理器,完成步骤2

/*** 退出成功处理器,用户退出成功后,执行此处理器*/
@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {//使用此工具类的对象进行序列化操作@Resourceprivate ObjectMapper objectMapper;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {//从请求头中获取Authorization信息String authorization = request.getHeader("Authorization");//如果授权信息为空,返回前端if(null==authorization){response.setCharacterEncoding("UTF-8");response.setContentType("application/json;charset=utf-8");HttpResult httpResult=HttpResult.builder().code(-1).msg("token不能为空").build();PrintWriter writer = response.getWriter();writer.write(objectMapper.writeValueAsString(httpResult));writer.flush();return;}//如果Authorization信息不为空,去掉头部的Bearer字符串String token = authorization.replace("Bearer ", "");//redis中删除token,这是关键点stringRedisTemplate.delete("logintoken:"+token);response.setCharacterEncoding("UTF-8");response.setContentType("application/json;charset=utf-8");HttpResult httpResult=HttpResult.builder().code(200).msg("退出成功").build();PrintWriter writer = response.getWriter();writer.write(objectMapper.writeValueAsString(httpResult));writer.flush();}
}

1.4 修改过滤器类实现步骤3,

每次登陆前先判断JWT是否存在, 仅当JWT存在的情况下才允许登录

@Resource
private StringRedisTemplate stringRedisTemplate;//        从redis中获取tokenString tokenInRedis = stringRedisTemplate.opsForValue().get("logintoken:" + jwtToken);if(!StringUtils.hasText(tokenInRedis)){printFront(response, "用户已退出,请重新登录");return;}

新JWTUtil

package com.sunsplanter.utils;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 本类用于生成和解析JWT*/
@Component
@Slf4j
public class JWTUtil {/*** 声明一个秘钥*/@Value("${my.secretKey}")private  String SECRET;/*** 生成JWT** @param userInfo   用户信息(包括编号和姓名)* @param auth     用户权限*/public String createToken(String userInfo, List<String> auth) {//得到当前的系统时间Date currentDate = new Date();//根据当前时间计算出过期时间 定死为5分钟Date expTime = new Date(currentDate.getTime() + (1000 * 60 * 5));//头数据就是算法alg和类型typ,其中type必须是JWT(用JWT生成token)Map<String, Object> header = new HashMap<>();header.put("alg", "HS256");header.put("typ", "JWT");return JWT.create().withHeader(header) //头.withClaim("user_info", userInfo) //自定义数据.withClaim("auth", auth) //自定义数据.withIssuedAt(currentDate) //创建时间.withExpiresAt(expTime)//过期时间.sign(Algorithm.HMAC256(SECRET));}/*** 验证JWT并解析** @param token 要验证的jwt的字符串*/public static Boolean verifyToken(String token) {try{// 使用秘钥创建一个解析对象JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(SECRET)).build();//验证JWTDecodedJWT decodedJWT = jwtVerifier.verify(token);System.out.println(decodedJWT);log.info("token验证正确");return true;}catch (TokenExpiredException e){e.printStackTrace();log.info("token验证失败");return false;}}/*** 从JWT里的获取用户信息(包括用户编号和用户名)*/public static String getUserInfoFromToken(String token){try{// 使用秘钥创建一个解析对象JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(SECRET)).build();//验证JWTDecodedJWT decodedJWT = jwtVerifier.verify(token);Claim username = decodedJWT.getClaim("user_info");return username.asString();}catch (TokenExpiredException e){e.printStackTrace();}return null;}/*** 从JWT里的获取用户权限*/public List<String> getAuth(String token){try{// 使用秘钥创建一个解析对象JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(SECRET)).build();//验证JWTDecodedJWT decodedJWT = jwtVerifier.verify(token);Claim auth = decodedJWT.getClaim("auth");return auth.asList(String.class);}catch (TokenExpiredException e){e.printStackTrace();}return null;}}

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/security_study?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootpassword: Mysql998168mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.sunsplanter.entityconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImplmy:secretKey: zheshi一个miyao

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

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

相关文章

GitLab安装配置

一、GitLab的简介 GitLab是开源的代码托管平台&#xff0c;提供版本控制功能、代码审查、持续集成等工具&#xff0c;帮助团队协作开发软件项目。用户可以创建仓库存储代码&#xff0c;管理问题追踪&#xff0c;部署自动化流程等。 二、GitLab的安装 1、Rocky_Linux 下载安装 …

c# B+树

B 树是一种自平衡的树数据结构&#xff0c;通常用于数据库和文件系统等需要大量数据插入、删除和搜索操作的场景。与 B 树不同的是&#xff0c;B 树的内部节点不存储数据&#xff0c;只用作索引&#xff0c;所有的数据都存储在叶子节点上。这种特性使得 B 树的数据检索效率更高…

应用回归分析:多重共线性

多重共线性的概念 在回归分析中&#xff0c;我们通常关注的是如何利用一个或多个自变量&#xff08;解释变量&#xff09;来预测一个因变量&#xff08;响应变量&#xff09;。当我们使用多元线性回归模型时&#xff0c;理想的情况是模型中的每一个自变量都能提供独特的、对因…

【嵌入式学习】C++QT-Day6-C++基础

作业&#xff1a; 1.思维导图 见我的博客&#xff1a;https://lingjun.life/wiki/EmbeddedNote/19Cpp 2.编程题&#xff1a; 以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了…

2024 前端面试题(GPT回答 + 示例代码 + 解释)No.114 - No.121

本文题目来源于全网收集&#xff0c;答案来源于 ChatGPT 和 博主&#xff08;的小部分……&#xff09; 格式&#xff1a;题目 h3 回答 text 参考大佬博客补充 text 示例代码 code 解释 quote 补充 quote 上一篇链接&#xff1a;2024 前端面试题&#xff08;GPT回答 示例…

汽车网络安全--关于供应商网络安全能力维度的思考

目录 1.关于CSMS的理解 2.OEM如何评审供应商 2.1 质量评审 2.2 网络安全能力评审 3.小结 1.关于CSMS的理解 最近在和朋友们交流汽车网络安全趋势时&#xff0c;讨论最多的是供应商如何向OEM证明其网络安全能力。 这是很重要的一环&#xff0c;因为随着汽车网络安全相关强…

三防平板电脑丨亿道工业三防平板丨三防平板定制丨机场维修应用

随着全球航空交通的增长和机场运营的扩展&#xff0c;机场维护的重要性日益凸显。为确保机场设施的安全和顺畅运行&#xff0c;采取适当的措施来加强机场维护至关重要。其中&#xff0c;三防平板是一种有效的工具&#xff0c;它可以提供持久耐用的表面保护&#xff0c;使机场维…

前端中的强缓存与协商缓存

强缓存&#xff1a;是直接从本地缓存中加载资源&#xff0c;不向服务器发起请求&#xff0c;除非过期了。这种方式可以减少网络延迟&#xff0c;提高页面加载速度&#xff0c;但无法保证资源的最新性 皮一下 : 不管你咋样&#xff0c;我都不搭理你,除非我真的无聊了&#xff0…

微信小程序 搜索框实现模糊搜索(带模拟数据,js,wxml,wxss齐全)

最近在做一个小程序的页面&#xff0c;搜索框困扰了我很久&#xff0c;今天终于把搜索框给做了出来&#xff0c;记录一下过程 我主要使用的就是wx的if&#xff0c;当我输入框用户点击的时候&#xff0c;我前面的显示界面添加上false属性&#xff0c;然后我搜索页面显示出true的…

小程序API能力汇总——基础容器API(三)

ty.getAccountInfo 获取小程序账号信息 需引入MiniKit&#xff0c;且在>3.1.0版本才可使用 参数 Object object 属性类型默认值必填说明completefunction否接口调用结束的回调函数&#xff08;调用成功、失败都会执行&#xff09;successfunction否接口调用成功的回调函数…

【Jvm】类加载机制(Class Loading Mechanism)原理及应用场景

文章目录 Jvm基本组成一.什么是JVM类的加载二.类的生命周期阶段1&#xff1a;加载阶段2&#xff1a;验证阶段3&#xff1a;准备阶段4&#xff1a;解析阶段5&#xff1a;初始化 三.类初始化时机四.类加载器1.引导类加载器&#xff08;Bootstrap Class Loader&#xff09;2.拓展类…

leetcode13题罗马数字转成整数

代码 public static int romanToInt(String s) {// 创建一个HashMap&#xff0c;将罗马数字字符映射为整数值HashMap<Character, Integer> map new HashMap<>();map.put(I, 1);map.put(V, 5);map.put(X, 10);map.put(L, 50);map.put(C, 100);map.put(D, 500);map.…

Eclipse 创建 Hello World 工程

Eclipse 创建 Hello World 工程 1. Hello WorldReferences Download and install the Eclipse IDE. 1. Hello World Eclipse -> double click -> Launch 单击蓝色方框 (右上角) 最大化 IDE File -> New -> C Project -> Finish Project name&#xff1a;工程名…

Matlab|基于支持向量机的电力短期负荷预测【最小二乘、标准粒子群、改进粒子群】

目录 主要内容 部分代码 结果一览 下载链接 主要内容 该程序主要是对电力短期负荷进行预测&#xff0c;采用三种方法&#xff0c;分别是最小二乘支持向量机&#xff08;LSSVM&#xff09;、标准粒子群算法支持向量机和改进粒子群算法支持向量机三种方法对负荷进行…

Python在无人炸弹

Python在无人炸弹研发开发中具有重要的作用。以下是几个方面的重要性&#xff1a; 简单易学的语法&#xff1a;Python是一种简单易学的编程语言&#xff0c;其语法清晰简洁&#xff0c;易于理解和编写。这使得开发人员能够更快速地实现想法和解决问题。 丰富的库和框架&#x…

LeetCode每日一题【209. 长度最小的子数组】

题目&#xff1a; 思路1&#xff1a;暴力循环 class Solution { public:int minSubArrayLen(int target, vector<int>& nums) {int len INT_MAX;for(int i0;i<nums.size();i){int sum 0;for(int ji;j<nums.size();j){sumnums[j];if(sum>target){len mi…

k8s的一些关键信息(归类摘抄,非提炼)

零&#xff1a;举例说明 当用户提交一个 Deployment 对象到 Kubernetes 集群时&#xff0c;控制平面的 API Server 接收到该请求&#xff0c;并将其转发给 Controller Manager。Controller Manager 中的 Deployment Controller 监听到该请求&#xff0c;并根据用户定义的配置信…

推荐彩虹知识付费商城免授权7.0源码

彩虹知识付费商城免授权7.0源码&#xff0c;最低配置环境 PHP7.2 1、上传源码到网站根目录&#xff0c;导入数据库文件&#xff1a;xydai.sql 2、修改数据库配置文件&#xff1a;/config.php 3、后台&#xff1a;/admin 账号&#xff1a;admin 密码&#xff1a;123456 4、前…

八、计算机视觉-边界填充

文章目录 前言一、原理二、具体的实现 前言 在Python中使用OpenCV进行边界填充&#xff08;也称为zero padding&#xff09;是一种常见的图像处理操作&#xff0c;通常用于在图像周围添加额外的像素以便进行卷积或其他操作。下面是使用OpenCV进行边界填充的基本原理和方法 一…

前端学习:jQuary的学习和使用

一、什么是jQuary jQuery 是一个 JavaScript 库。jQuery 极大地简化了 JavaScript 编程。jQuery 很容易学习。 二、jQuary和原生js对比获取input框中的数据和div框中的数据 <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <tit…