jwttoken+redis+springsecurity

思路

jwttoken不设置过期时间
redis管理过期时间,并且续签
redis中key="login:"+userId, value=jwtUser
再次访问时,解析token中userId,并且根据过期时间自动续签

JWT 实现登录认证 + Token 自动续期方案
pom文件配置

<!--Redis-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.3.5.RELEASE</version>
</dependency><!--JWT-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.10.7</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.10.7</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.10.7</version><scope>runtime</scope>
</dependency><!--spring security-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

application.yml配置

spring:redis:database: 0host: redis_ipport: redis_portconnect-timeout: 30000lettuce:pool:# 最大阻塞等待时间,负数表示没有限制max-wait: -1# 连接池中的最大空闲连接max-idle: 10# 连接池中的最小空闲连接min-idle: 0# 连接池中最大连接数,负数表示没有限制max-active: 50password: redis_password

service层logon方法

@Override
public Object login(LoginDTO loginDTO) {UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginDTO.getUsername(), loginDTO.getPassword());Authentication authenticate;try {// 对登录用户进行认证authenticate = authenticationManager.authenticate(authenticationToken);} catch (AuthenticationException ex) {throw new ServiceException(ex.getMessage());}if (Objects.isNull(authenticate)) {throw new ServiceException("登录失败");}JWTUser jwtUser = (JWTUser)authenticate.getPrincipal();if(jwtUser.getUserDO().getDeleteFlag()!=0){throw new ServiceException("用户已被停用,如需开启,请联系管理员");}LoginSuccessDTO loginSuccessDTO = new LoginSuccessDTO();loginSuccessDTO.setUserId(jwtUser.getId());loginSuccessDTO.setUsername(jwtUser.getUsername());loginSuccessDTO.setNickName(jwtUser.getUserDO().getNickName());loginSuccessDTO.setUserRealName(jwtUser.getUserDO().getUserRealName());loginSuccessDTO.setDepartment(jwtUser.getUserDO().getDepartment());Map<String, Object> userInfo = DataConvert.BeanPropertyNameTraverse(loginSuccessDTO);String token = JwtUtils.generateToken(userInfo);String userId = jwtUser.getId();Date expirationTime = JwtUtils.getExpirationTime();redisUtils.set("login:" + userId, jwtUser, AuthConstant.EXPIRATION_TIME_IN_SECOND, TimeUnit.SECONDS);loginSuccessDTO.setLoginTime(new Date());loginSuccessDTO.setExpireTime(expirationTime);loginSuccessDTO.setToken(token);return loginSuccessDTO;
}
@Override
public Object logout() {/** 获取SecurityContextHolder中的userId。* 注销操作也是需要携带token的,spring security已经在内部对token进行验证,* 并能够从redis取出用户信息*/UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();JWTUser jwtUser = (JWTUser)authentication.getPrincipal();String userId = jwtUser.getId();redisUtils.del("login:"+userId);return "注销成功";
}

token工具类

package cma.sxqxgxw.utils.jwt;import cma.sxqxgxw.auth.constant.AuthConstant;
import cma.sxqxgxw.common.exception.ServiceException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;import javax.crypto.SecretKey;
import java.util.Map;
import java.util.Date;public class JwtUtils {private static final String SIGNATURE = "!QW@f5g%T^U&f3r32523534634634654754ygrgfdjjuhfdsdf6";/*** 计算token的过期时间** @return 过期时间*/public static Date getExpirationTime() {return new Date(System.currentTimeMillis() + AuthConstant.EXPIRATION_TIME_IN_SECOND * 1000);}/*** 生成Token* @param claims 用户信息* @return token*/public static String generateToken(Map<String, Object> claims) {Date createdTime = new Date();//Date expirationTime = getExpirationTime();byte[] keyBytes = SIGNATURE.getBytes();SecretKey key = Keys.hmacShaKeyFor(keyBytes);return Jwts.builder().setClaims(claims).setIssuedAt(createdTime)//.setExpiration(expirationTime).signWith(key, SignatureAlgorithm.HS256).compact();}/*** 从token中获取claim** @param token token* @return claim*/public static Claims getClaimsFromToken(String token) {try {return Jwts.parser().setSigningKey(SIGNATURE.getBytes()).parseClaimsJws(token).getBody();} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {throw new ServiceException("Token invalided.");}}/*** 获取token的过期时间** @param token token* @return 过期时间*/public static Date getExpirationDateFromToken(String token) {return getClaimsFromToken(token).getExpiration();}/*** 判断token是否过期** @param token token* @return 已过期返回true,未过期返回false*/public static Boolean isTokenExpired(String token) {Date expiration = getExpirationDateFromToken(token);return expiration.before(new Date());}/*** 判断token是否非法** @param token token* @return 未过期返回true,否则返回false*/public static Boolean validateToken(String token) {System.out.println("token valid");return !isTokenExpired(token);}
}

redis工具类

@Slf4j
@Component
public class RedisUtils {private final RedisTemplate<String, Object> redisTemplate;@Autowiredpublic RedisUtils(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}/*** 指定缓存失效时间** @param key  键* @param time 时间(秒)* @return*/public boolean expire(String key, long time) {try {if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间** @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {log.info("Redis连接失败,可能是Redis服务未启动。" + e.getMessage());return false;}}/*** 删除缓存** @param key 可以传一个值 或多个*/@SuppressWarnings("unchecked")public boolean del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {Boolean delete = redisTemplate.delete(key[0]);return delete;} else {Long delete = redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));return true;}}return false;}//============================String=============================/*** 普通缓存获取** @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** @param key   键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {log.info("插入操作时,Redis连接失败,可能是Redis服务未启动。" + e.getMessage());return false;}}/*** 普通缓存放入并设置时间** @param key   键* @param value 值* @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time, TimeUnit timeUnit) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, timeUnit);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}
}

jwt过滤器
生成的token中不带有过期时间,token的过期时间由redis进行管理
当redis过期时间小于10分钟时,redis过期时间续签30分钟

@Component
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {private final RedisUtils redisUtils;@Autowiredpublic JWTAuthorizationFilter(AuthenticationManager authenticationManager, RedisUtils redisUtils) {super(authenticationManager);this.redisUtils = redisUtils;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {String token = request.getHeader("token");if (!StringUtils.hasText(token)) {request.setAttribute("errorMessage", "Token缺失,请检查请求Header");chain.doFilter(request, response);return;}try {Claims claims = JwtUtils.getClaimsFromToken(token);String userId = (String) claims.get("userId");boolean hasKey = redisUtils.hasKey("login:" + userId);if (!hasKey) {request.setAttribute("errorMessage", "Token无效或已经过期");throw new ServiceException("Token无效或已经过期");}// 令牌续签long expire = redisUtils.getExpire("login:" + userId);System.out.println("剩余时间: "+expire);if (expire < AuthConstant.RENEWAL_TIME_IN_SECOND) {redisUtils.expire("login:" + userId, AuthConstant.EXPIRATION_TIME_IN_SECOND);System.out.println("令牌续签" + AuthConstant.EXPIRATION_TIME_IN_SECOND + "秒");}JWTUser jwtUser = (JWTUser) redisUtils.get("login:" + userId);UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities());// 设置认证状态SecurityContextHolder.getContext().setAuthentication(authenticationToken);chain.doFilter(request, response);} catch (ServiceException e) {request.setAttribute("errorMessage", e.getMessage());chain.doFilter(request, response);}}
}

过期时间常量

public class AuthConstant {/*** 令牌有效时长*/public static final Long EXPIRATION_TIME_IN_SECOND = 60*30L;/*** 过期时间剩余多少时间时续签*/public static final Long RENEWAL_TIME_IN_SECOND = 60*10L;
}

Redis配置类

package cma.sxqxgxw.common.config.redis;import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** Redis配置* 主要指定自定义序列化器,避免序列化反序列化失败*/
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();// 参照StringRedisTemplate内部实现指定序列化器redisTemplate.setConnectionFactory(redisConnectionFactory);// 设置key的序列化器redisTemplate.setKeySerializer(keySerializer());redisTemplate.setHashKeySerializer(keySerializer());// 设置value的序列化器// 解决autoType is not support.xx.xx的问题String[] acceptNames = {"org.springframework.security.core.authority.SimpleGrantedAuthority"};GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer(acceptNames);redisTemplate.setValueSerializer(serializer);redisTemplate.setHashValueSerializer(serializer);return redisTemplate;}private RedisSerializer<String> keySerializer(){return new StringRedisSerializer();}//使用Jackson序列化器private RedisSerializer<Object> valueSerializer(){return new GenericJackson2JsonRedisSerializer();}
}

Jwt拦截器

package cma.sxqxgxw.common.interceptor;import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class JwtInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {return true;}}

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

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

相关文章

XLSX json转文本流 json转文件

封装一个公共 xlsx.js import { read, utils, writeFile } from xlsx // 文本流转json // 入参&#xff1a;文本流 回调 入参{} export const readJsonFromFile (file, callBackFn, opt) > {const reader new FileReader()reader.readAsArrayBuffer(file)reader.onload …

vue原生实现element上传多张图片浏览删除

vue原生实现element上传多张图片浏览删除 <div class"updata-component" style"width:100%;"><div class"demo-upload-box clearfix"><div class"demo-upload-image-box" v-if"imageUrlArr && imageUrlAr…

点云从入门到精通技术详解100篇-点云特征学习模型及其在配准中的应用(续)

目录 基于局部邻域的点云特征学习模型 3.1引言 3.2自适应邻域选择算法

算法-动态规划-编辑距离

算法-动态规划-编辑距离 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/longest-increasing-subsequence/ 1.2 题目描述 2 动态规划 2.1 思路 dp[i][j] 表示 word1[0,i) 变换为 word2[0,j)的最少步数&#xff0c;那么转移表达式&#xff1a; i和j上的字符相同时…

[网鼎杯 2018]Comment git泄露 / 恢复 二次注入 .DS_Store bash_history文件查看

首先我们看到账号密码有提示了 我们bp爆破一下 我首先对数字爆破 因为全字符的话太多了 爆出来了哦 所以账号密码也出来了 zhangwei zhangwei666 没有什么用啊 扫一下吧 有git git泄露 那泄露看看 真有 <?php include "mysql.php"; session_start(); if(…

子层连接结构

目录 1、子层连接结构介绍 2、子层连接结构 3、代码实现 1、子层连接结构介绍 输入到每个子层以及规范化层的过程中&#xff0c;还使用了残差连接&#xff08;跳跃连接&#xff09;&#xff0c;因此我们把这一部分整体结构叫子层连接&#xff08;代表子层及其连接结构&#xf…

常见Http请求形式

一、请求参数的类型 我们在做boot项目时&#xff0c;常常会向接口发起请求&#xff0c;有些请求需要附带一些参数&#xff0c;比如说分页查询&#xff0c;就需要带上pageNum(当前页)和pageSize(页面大小)等参数 有两种方式可以传递这样的参数 query类型&#xff0c;参数通过…

Spring面试题

一、Spring面试题 1、Bean的生命周期 1.调用bean的构造方法创建Bean 2.通过反射调用Set方法设 bean的属性 3.如果Bean 实现了实现了BeanNameAware接口&#xff0c;Spring将调用setBeanName()&#xff0c;设置 Bean的name 4.如果Bean实现了BeanFactoryAware接口&#xff0c…

Linux命令(100)之sz

linux命令之sz 1.sz介绍 linux命令sz是用来把文件从Linux平台下载到Windows上 2.sz用法 sz [参数] file sz参数 参数说明-b使用binary的方式下载&#xff0c;不解释字符为ascii-y相同文件名&#xff0c;覆盖-E相同文件名&#xff0c;不会将其覆盖&#xff0c;而是会在所上传…

数据结构 - 二叉树

递归实现前中后序遍历 #include<stdio.h> #include<stdlib.h>#define TElemType inttypedef struct BiTNode{TElemType data;struct BiTNode *lchild,*rchild; }BiTNode,*BiTree; BiTNode root;void visit(TElemType& e){printf("%d",e); }void Pre…

iPhone 15分辨率,屏幕尺寸,PPI 详细数据对比 iPhone 15 Plus、iPhone 15 Pro、iPhone 15 Pro Max

史上最全iPhone 机型分辨率&#xff0c;屏幕尺寸&#xff0c;PPI详细数据&#xff01;已更新到iPhone 15系列&#xff01; 点击放大查看高清图 &#xff01;

MDK自动生成带校验带SVN版本号的升级文件

MDK自动生成带校验带SVN版本号的升级文件 获取SVN版本信息 确保SVN安装了命令行工具&#xff0c;默认安装时不会安装命令行工具 编写一个模板头文件 svn_version.temp.h, 版本号格式为 1_0_0_SVN版本号 #ifndef __SVN_VERSION_H #define __SVN_VERSION_H#define SVN_REVISIO…

web前端面试-- js深拷贝的一些bug,特殊对象属性(RegExp,Date,Error,Symbol,Function)处理,循环引用weekmap处理

本人是一个web前端开发工程师&#xff0c;主要是vue框架&#xff0c;整理了一些面试题&#xff0c;今后也会一直更新&#xff0c;有好题目的同学欢迎评论区分享 ;-&#xff09; web面试题专栏&#xff1a;点击此处 文章目录 深拷贝和浅拷贝的区别浅拷贝示例深拷贝示例 特殊对象…

ODrive移植keil(五)—— 开环控制和电流变换

目录 一、开环控制1.1、控制原理1.2、硬件接线1.3、代码说明1.4、程序演示1.5、程序架构的体现 二、电流变换2.1、理论说明2.2、代码说明 ODrive、VESC和SimpleFOC 教程链接汇总&#xff1a;请点击 一、开环控制 在SimpleFOC系列中有开环控制的教程&#xff0c;SimpleFOC移植S…

linux修改docker容器时间

1.查看宿主机系统时间 date -R 2.进入docker容器查看时间 docker exec -it -u root 容器id /bin/bash cat /etc/timezone &#xff08;容器内部系统时区显示Etc/UTC即会造成时间错误&#xff09; 3.创建容器时间 echo Asia/Shanghai >/etc/timezone 4.退出容器 exit 5.复…

【C进阶】内存函数

strcpy拷贝的仅仅是字符串&#xff0c;但是内存中的数据不仅仅是字符&#xff0c;所以就有了memcpy函数 1. memcpy void *memcpy &#xff08;void * destination &#xff0c;const void * source , size_t num) 函数memcpy从source的位置开始向后拷贝num个字节的数据到desti…

基于nodejs+vue驾校预约管理系统

通过科技手段提高自身的优势&#xff1b;对于驾校预约管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了驾校预约管理系统&#xff0c; 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;驾校预约管理系统&am…

1.go web之gin框架

Gin框架 一、准备 1.下载依赖 go get -u github.com/gin-gonic/gin2.引入依赖 import "github.com/gin-gonic/gin"3. &#xff08;可选&#xff09;如果使用诸如 http.StatusOK 之类的常量&#xff0c;则需要引入 net/http 包 import "net/http"二、基…

python之K线模式识别

1、晨星 晨星也称作早晨之星&#xff0c;它是一种三日形态的K线组合&#xff0c;第一日是阴线&#xff0c;第二日价格振幅较小&#xff0c;第三日出现阳线&#xff0c;它的一般形态如下图所示。晨星的K线组合形态一般出现在下跌的趋势之后&#xff0c;预示着价格的上升回调。其…

matlab 2ask 4ask 信号调制

1 matlab 2ask close all clear all clcL =1000;Rb=2822400;%码元速率 Fs =Rb*8; Fc=Rb*30;%载波频率 Ld =L*Fs/Rb;%产生载波信号 t =0:1/Fs:L/Rb;carrier&