当前位置: 首页 > news >正文

用户管理——认证功能JWT和Session

目录

    • 用户认证功能的技术选型
    • JWT和Session的区别
    • 基于JWT和Session的认证流程
      • 基于JWT的认证流程
      • 基于Session的认证流程
    • 基于JWT和Session的认证的优缺点
    • 基于JWT和Session的认证的安全性
    • 基于JWT和Session的认证的性能分析
    • 基于JWT的一次性和无法废弃
    • 基于JWT和Session的认证的续签
    • 选择
    • JWT功能实现

用户认证功能的技术选型

  我们要实现用户的认证功能,很容易就想到使用JWT或者Session,但是我们在确定我们的技术选型之前,我们需要了解他们。
 
 

JWT和Session的区别

  基于JWT和Session的方式的主要区别就是用户状态的保存位置JWT是保存在客户端的,而Session是保存在服务端的
 
 

基于JWT和Session的认证流程

基于JWT的认证流程

1、用户在浏览器中输入用户名和密码,服务器通过密码校验之后生成一个token并保存到数据库,并将这个token返回给前端。
2、前端获取到token,将其存储到cookie或者local storage中,在后续的请求中都带着这个token信息进行访问。
3、服务器获取token值,通过查找数据判断当前的token是否有效。
 

基于Session的认证流程

1、用户在浏览器输入用户名和密码,服务器通过密码校验之后生成一个session保存到数据库。
2、服务器为用户生成一个sessionId,并将具有sessionId的cookie放置在用户浏览器中,在后续的请求中都带这个cookie信息进行访问。
3、服务器获取cookie,通过获取cookie中的sessionId查找数据库判断当前请求是否有效。

基于JWT和Session的认证的优缺点

  • JWT保存在客户端,在分布式环境下不需要做额外的工作。
  • Session保存在服务器,在分布式环境下需要实现多台机器共享Session,并且一般都需要结合Cookie实现认证,所以需要浏览器支持cookie,所以移动端无法使用session认证方案。

基于JWT和Session的认证的安全性

  • JWT是使用base64编码的,因此在JWT中不能存在敏感数据。如果在JWT中存储了敏感信息,可以解码出来是非常的不安全,比如用户的密码。
  • Session的信息是保存在服务器的,相对来说更安全。

基于JWT和Session的认证的性能分析

  • 经过编码之后的JWT是一个非常长的字符串,由于cookie的限制大小一般是4K,cookie很有可能放不下,所以JWT一般放在客户端的local storage里面。并且用户在系统中的每一次http请求请求都会把JWT携带在Header里面,HTTP请求的Header可能比Body还有大。
  • SessionId只是很短的一个字符串,所以JWT的HTTP请求比使用Session的开销大很多。

基于JWT的一次性和无法废弃

JWT是一次性的,想要修改里面的内容,只能重新生成一个JWT。
生成的一个JWT,在到过期之后就会始终有效,无法中途废弃。如果想中途废弃,一种常用的处理手段就是结合redis。

基于JWT和Session的认证的续签

Session的有效时间一般是30分钟,如果在30分钟内有访问,有效期会被刷新致30分钟。
而如果我们要改变JWT的有效时间,有两种方式:
1、最简单的一种方式是每次请求刷新JWT,即每个HTTP请求都返回一个新的JWT。这个方法不仅暴力不优雅,而且每次请求都要做JWT的加密解密,会带来性能问题。
2、我们可以在Redis中单独为每个JWT设置过期时间,每次访问的时候刷新JWT的过期时间。

 
 

选择

我投JWT一票,JWT有很多缺点,但是JWT在分布式环境下不需要像Session一样额外实现多机数据共享,不需要额外的工作,使用JWT不香吗?且JWT一次性的缺点可以结合redis进行弥补。

JWT功能实现

JWT所需依赖

public class JWTUtil {private static final Logger logger = LoggerFactory.getLogger(JWTUtil.class);//私钥private static final String TOKEN_SECRET = "123456";/*** 生成token,自定义过期时间 毫秒** @param userTokenDTO* @return*/public static String generateToken(UserTokenDTO userTokenDTO) {try {// 私钥和加密算法Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);// 设置头部信息Map<String, Object> header = new HashMap<>(2);header.put("Type", "Jwt");header.put("alg", "HS256");return JWT.create().withHeader(header).withClaim("token", JSONObject.toJSONString(userTokenDTO))//.withExpiresAt(date).sign(algorithm);} catch (Exception e) {logger.error("generate token occur error, error is:{}", e);return null;}}/*** 检验token是否正确** @param token* @return*/public static UserTokenDTO parseToken(String token) {Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);JWTVerifier verifier = JWT.require(algorithm).build();DecodedJWT jwt = verifier.verify(token);String tokenInfo = jwt.getClaim("token").asString();return JSON.parseObject(tokenInfo, UserTokenDTO.class);}
}

说明:

  • 生成的token中不带有过期时间,token的过期时间由redis进行管理。
  • UserTokenDTO中不带有敏感信息,如password字段不会出现在token中。

Redis工具类

public final class RedisServiceImpl implements RedisService {/*** 过期时长*/private final Long DURATION = 1 * 24 * 60 * 60 * 1000L;@Resourceprivate RedisTemplate redisTemplate;private ValueOperations<String, String> valueOperations;@PostConstructpublic void init() {RedisSerializer redisSerializer = new StringRedisSerializer();redisTemplate.setKeySerializer(redisSerializer);redisTemplate.setValueSerializer(redisSerializer);redisTemplate.setHashKeySerializer(redisSerializer);redisTemplate.setHashValueSerializer(redisSerializer);valueOperations = redisTemplate.opsForValue();}@Overridepublic void set(String key, String value) {valueOperations.set(key, value, DURATION, TimeUnit.MILLISECONDS);log.info("key={}, value is: {} into redis cache", key, value);}@Overridepublic String get(String key) {String redisValue = valueOperations.get(key);log.info("get from redis, value is: {}", redisValue);return redisValue;}@Overridepublic boolean delete(String key) {boolean result = redisTemplate.delete(key);log.info("delete from redis, key is: {}", key);return result;}@Overridepublic Long getExpireTime(String key) {return valueOperations.getOperations().getExpire(key);}
}

业务实现

登陆功能

public String login(LoginUserVO loginUserVO) {//1.判断用户名密码是否正确UserPO userPO = userMapper.getByUsername(loginUserVO.getUsername());if (userPO == null) {throw new UserException(ErrorCodeEnum.TNP1001001);}if (!loginUserVO.getPassword().equals(userPO.getPassword())) {throw new UserException(ErrorCodeEnum.TNP1001002);}//2.用户名密码正确生成tokenUserTokenDTO userTokenDTO = new UserTokenDTO();PropertiesUtil.copyProperties(userTokenDTO, loginUserVO);userTokenDTO.setId(userPO.getId());userTokenDTO.setGmtCreate(System.currentTimeMillis());String token = JWTUtil.generateToken(userTokenDTO);//3.存入token至redisredisService.set(userPO.getId(), token);return token;
}

说明:

  • 判断用户名密码是否正确。
  • 用户名密码正确则生成token。
  • 将生成的token保存至redis。

登出功能

public boolean loginOut(String id) {boolean result = redisService.delete(id);if (!redisService.delete(id)) {throw new UserException(ErrorCodeEnum.TNP1001003);}return result;
}

将对应的key删除即可

更新密码功能

public String updatePassword(UpdatePasswordUserVO updatePasswordUserVO) {//1.修改密码UserPO userPO = UserPO.builder().password(updatePasswordUserVO.getPassword()).id(updatePasswordUserVO.getId()).build();UserPO user = userMapper.getById(updatePasswordUserVO.getId());if (user == null) {throw new UserException(ErrorCodeEnum.TNP1001001);}if (userMapper.updatePassword(userPO) != 1) {throw new UserException(ErrorCodeEnum.TNP1001005);}//2.生成新的tokenUserTokenDTO userTokenDTO = UserTokenDTO.builder().id(updatePasswordUserVO.getId()).username(user.getUsername()).gmtCreate(System.currentTimeMillis()).build();String token = JWTUtil.generateToken(userTokenDTO);//3.更新tokenredisService.set(user.getId(), token);return token;
}

说明:

  • 更新用户密码时需要重新生成新的token,并将新的token返回给前端,由前端更新保存在local storage中的token,同时更新存储在redis中的token,这样实现可以避免用户重新登陆,用户体验感不至于太差。

拦截器类

public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {String authToken = request.getHeader("Authorization");String token = authToken.substring("Bearer".length() + 1).trim();UserTokenDTO userTokenDTO = JWTUtil.parseToken(token);//1.判断请求是否有效if (redisService.get(userTokenDTO.getId()) == null || !redisService.get(userTokenDTO.getId()).equals(token)) {return false;}//2.判断是否需要续期if (redisService.getExpireTime(userTokenDTO.getId()) < 1 * 60 * 30) {redisService.set(userTokenDTO.getId(), token);log.error("update token info, id is:{}, user info is:{}", userTokenDTO.getId(), token);}return true;
}

说明:拦截器中主要做两件事,一是对token进行校验,二是判断token是否需要进行续期

token校验:

  • 判断id对应的token是否不存在,不存在则token过期
  • 若token存在则比较token是否一致,保证同一时间只有一个用户操作

token自动续期:
为了不频繁操作redis,只有当离过期时间只有30分钟时才更新过期时间。

拦截器配置类

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(authenticateInterceptor()).excludePathPatterns("/logout/**").excludePathPatterns("/login/**").addPathPatterns("/**");}@Beanpublic AuthenticateInterceptor authenticateInterceptor() {return new AuthenticateInterceptor();}
}

  
  
  

相关文章:

用户管理——认证功能JWT和Session

目录用户认证功能的技术选型JWT和Session的区别基于JWT和Session的认证流程基于JWT的认证流程基于Session的认证流程基于JWT和Session的认证的优缺点基于JWT和Session的认证的安全性基于JWT和Session的认证的性能分析基于JWT的一次性和无法废弃基于JWT和Session的认证的续签选择…...

hashlib — 加密哈希算法

hashlib — 加密哈希算法 1.概述 加密可以保护消息的安全&#xff0c;以便验证它们的准确性并且使它们受保护不被拦截。 Python 的加密方式支持包括利用像 MD5 和 SHA 这样的标准算法对消息内容产生签名的 hashlib 和验证消息没有在传输过程中被改变的 hmac hashlib 哈希库模…...

四喜临门选股预警源码指标

{四喜临门选股预警} AP1:CROSS(MA(C,5),MA(C,10)); RSV:(CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100; K:SMA(RSV,3,1); D:SMA(K,3,1); AP2:CROSS(K,D); DIFF:EMA(CLOSE,12) - EMA(CLOSE,26); DEA:EMA(DIFF,9); AP3:CROSS(DIFF,DEA); AP4:CROSS(MA(V,5),MA(V,10)); GYTJ1:…...

Kotlin新手教程五(扩展)

一、扩展 在Kotlin中可以给一个类添加一个新的方法而不用继承该类或者使用设计模式&#xff0c;这样的方法称为扩展。 1.扩展函数 声明一个扩展函数&#xff0c;我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。 下面代码为 MutableList 添加一个swap 函数&am…...

QT入门Containers之Widget、Frame

目录 一、QWidget界面相关 1、布局介绍 2、基本界面属性 3、特殊属性 二、QFrame 三、Demo展示 此文为作者原创&#xff0c;创作不易&#xff0c;转载请标明出处&#xff01; 一、QWidget界面相关 1、布局介绍 为什么将QWidget容器放在第一个&#xff0c;因为目前使用过…...

数据结构与算法基础-学习-12-线性表之顺序队

一、个人理解队列是线性表的衍生之一&#xff0c;具有先进先出的特性&#xff0c;在队尾进行插入操作&#xff0c;在队头进行删除操作。队列的存储结构分为两个大类&#xff0c;一种是顺序队&#xff0c;就是用数组实现。另一种就是链队&#xff0c;使用链表实现。顺序队存在真…...

Python 字典(Dictionary)小窍门

字典是另一种可变容器模型&#xff0c;且可存储任意类型对象。字典的每个键值 key:value 对用冒号 : 分割&#xff0c;每个键值对之间用逗号 , 分割&#xff0c;整个字典包括在花括号 {} 中 ,格式如下所示&#xff1a;d {key1 : value1, key2 : value2 }注意&#xff1a;dict …...

知识图谱构建技术综述

摘要 *知识图谱为实现语义化智能搜索以及知识互联打下了基础&#xff0c;。&#xff0c; *随着知识的发展&#xff0c;传统的基于模板和规则构建的知识图谱已经被深度学习所替代。 知识组织得原则中&#xff1a;知识的充分性、有序性和标准化规则。深度学习的效果在很大程度上…...

环境变量和进程地址空间

目录 环境变量&#xff1a; env&#xff1a;显示所有的环境变量&#xff1a; echo $环境变量名表示查看环境变量的值 理解环境变量&#xff1a; getenv&#xff1a;显示环境变量的值 export set命令&#xff1a;显示所有变量 unset取消变量&#xff1a; pwd&#xff1a;当…...

【数据结构】栈和队列

目录 一、栈 1、栈的定义 2、栈的模拟实现&#xff08;顺序栈&#xff09; 1、创建一个顺序结构的栈 2、实现压栈方法&#xff08;push&#xff09; 3、模拟实现pop方法&#xff08;出栈&#xff09; 4、模拟实现peek(查看) 5、测试上述方法 3、栈的应用场景 1、改变元…...

sql复习(视图、Top-N分析、其他数据库对象)

一、视图view 1.视图定义 视图是一种虚表。 视图建立在已有表的基础上, 视图赖以建立的这些表称为基表。 向视图提供数据内容的语句为 SELECT 语句, 可以将视图理解为存储起来的 SELECT 语句。 视图向用户提供基表数据的另一种表现形式。 2.使用视图的好处 控制数据访问 简…...

2023年私募股权基金研究报告

第一章 概况 PE是私募&#xff0c;也即私募投资基金&#xff0c;是指以非公开发行方式向合格投资者募集的&#xff0c;投资于股票、股权、债券、期货、期权、基金份额及投资合同约定的其他投资标的&#xff08;如艺术品、红酒等&#xff09;的投资基金&#xff0c;简称私募基金…...

Redis单点故障+红锁原理

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、Redis单点故障二、红锁原理三、Redission实现了红锁一、Redis单点故障 单台redis容易出单点故障采用集群,获取到锁之后数据持久化到rdb,aof文件中从节点有可能在从主节点拿到数据之前,主节点…...

数据库中的存储过程

1、创建存储过程create procedure sp_name[参数名] [类型],[参数名] [类型]asbegin.........end以上格式还可以简写成&#xff1a;create proc sp_name[参数名] [类型],[参数名] [类型]asbegin.........end/*注&#xff1a;“sp_name”为需要创建的存储过程的名字&#xff0c;该…...

基于 VPX 总线的工件台运动控制系统研究与开发-DSP+FPGA硬件架构(一)

作为光刻机核心单元之一&#xff0c;超精密工件台主要负责实现快速扫描、上下片、精密定位、调平调焦等功能。目前&#xff0c;较为成熟的方案大多采用 VME 并行总线架构来建立超精密工件台控制系统&#xff0c;由于随着系统性能要求的提升&#xff0c;VME 总线以及相应的处理器…...

Android 9.0 根据包名授予app所需的权限

1.概述 在9.0的系统rom产品定制化开发中,在对系统app首次启动默认是会弹出授权的弹窗的,但是对于产品来说会显示的有些麻烦,对产品体验度也不是很好,所以在进行产品开发的时候,默认要求对一些app根据包名授予权限,这样就不会弹出授权的窗口了默认就有权限了,接下来就来实…...

如何将Python包发布到PyPI上,使用pip安装自己的库

如何发布自己的第三方库1. PyPi的用途2.Python包发布步骤2.1 创建目录结构2.2 准备文件1、README.rst2、LICENSE.txt&#xff0c;创建许可证3、setup.py文件4.克隆setup.py仓库&#xff08;推荐&#xff09;2.3 编写核心代码2.4 生成分发档案2.5 发布包到PyPi3.验证发布PYPI成功…...

【Git】git常用命令总结

简言 git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 里面有很多常用的命令语法&#xff0c;在此做一个常用命令总结记录&#xff0c;以备不时之需。 命令总结 由于git是基于linux开发的工具&#xff0c;所以有个特点&a…...

Cortex-M0中断控制和系统控制

目录1.NVIC和系统控制块特性2.中断使能和清除使能3.中断挂起和清除挂起4.中断优先级5.中断控制的通用汇编代码使能和禁止中断设置和清除中断挂起状态设置中断优先级6.异常屏蔽寄存器&#xff08;PRIMASK&#xff09;7.中断输入和挂起行为8.中断等待9.系统异常的控制寄存器10.系…...

科技云报道:2023,云计算的风向变了

科技云报道原创。 2022&#xff0c;是云计算的“分水岭”之年。 与前两年的火热相比&#xff0c;2022年云计算行业实属不太好过&#xff1a;阿里云一季度营收增速创出历史新低&#xff0c;腾讯云的市场份额也被后来者华为云反超&#xff0c;沦为第三。 在此情形下&#xff0c…...

工程管理系统源码-专注项目数字化管理-工程管理

工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#xff1a;实现对数据字典标签的增删改查操作 2、编码管理&#xff1a;实现对系统编码的增删改查操作 3、用户管理&#xff1a;管理和查看用户角色 4、菜单管理&#xff1a;实现对系统菜单的增删改查操…...

Nacos详细使用操作文档(图文详细)

文章目录Nacos详细使用操作文档(图文详细)1、安装2、Nacos作为注册中心2.1、Nacos服务注册【ICRMS】2.2、Nacos 服务调用2.2.1、Feign 远程调用【Personnel】2.2.2)、RestTemplateRibbon 远程调用【Personnel】3、Nacos作为配置中心4、Nacos 命令空间5、Nacos配置文件参数详解N…...

如何评价2023年美赛ABC题目

A题 遭受干旱侵袭的植物群落 背景 不同种类的植物对压力的反应方式不同。例如&#xff0c;草原对干旱非常敏感。干旱发生的频率和严重 程度各不相同。大量的观察表明&#xff0c;不同物种的数量在植物群落如何适应连续几代的干旱周期中 起着重要作用。在一些只有一种植物的…...

Win10显示dds及tga缩略图

整理之前做游戏MOD时收集的模型资源,3D游戏模型的贴图文件格式基本都是dds或tga的,毕竟无损压缩、支持嵌入MipMap、带透明通道、可以被GPU硬解balabala...道理我都懂但这俩玩意系统根本直接查看不了,就算装上专门的看图软件或插件,文件夹视图下也没有缩略图预览,只能一个个点开…...

Lesson5.1---Python 之 NumPy 简介和创建数组

一、NumPy 简介 NumPy&#xff08;Numerical Python&#xff09;是 Python 的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵&#xff0c;比 Python 自身的嵌套列表&#xff08;nested list structure&#xff09;结构要高效的多&#xff08;该结构也可以用来表示…...

Exchange 2013升级以及域名绑定等若干问题

环境简介Exchange 2013服务器位于ad域中&#xff0c;系统为Windows server 2012 R2&#xff0c;其内部域名为&#xff1a;mail.ad.com一. Exchange客户端无法在浏览器中正常运行在域中部署Exchange服务器后&#xff0c;除了可以通过outlook、foxmail等邮件客户端来使用邮箱功能…...

linux安装jenkins

1. 官网寻找安装方式 进入到jenkins官网&#xff0c;找到对应的下载页面&#xff1a;https://www.jenkins.io/download/ 根据自己系统还有想要使用的版本&#xff0c;进行选择即可。这里我们使用CentOS作为示例&#xff0c;版本选择长期支持版&#xff08;LTS&#xff09; 2.…...

【MySQL】MySQL表的增删改查(CRUD)

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;MySQL数据库&#x1f447; ✨算法专栏&#xff1a;算法基础&#x1f447; ✨每日一语&#xff1a;生命久如暗室&#xff0c;不碍朝歌暮诗 目 录&#x1f513;一. CRUD&#x1f512;二. 新增&#xff08;Creat…...

GCC for openEuler 数据库性能优化实践

GCC for openEuler是基于开源GCC开发的编译器工具链&#xff08;包含编译器&#xff0c;汇编器&#xff0c;链接器&#xff09;&#xff0c;在openEuler社区开源发布&#xff0c;并通过鲲鹏社区免费提供二进制包&#xff0c;支持aarch64处理器架构。 关键特性 支持鲲鹏微架构芯…...

【C++】类和对象(第二篇)

文章目录1. 类的6个默认成员函数2. 构造函数2.1 构造函数的引出2.2 构造函数的特性3. 析构函数3.1 析构函数的引出3.2 析构函数的特性4. 拷贝构造函数4.1 概念4.2 特性5.赋值运算符重载5.1 运算符重载概念注意练习5.2 赋值重载实现赋值重载的特性6. const成员函数7. 取地址及co…...

用软件做网站/网络营销推广公司网站

一切障碍都可以击倒我 ——K 这是一个才华横溢的青年 而这仅仅是一个故事的开始田地里生长着茁壮的少年们少年盛开着他们希望成为的颜色从脚跟泛滥到人中 青涩的胡须疯长顶向十几年的伞状自由连带它们的主人落入自己的郊…...

芸志建站怎么建立网站/地推团队联系方式

本文实例讲述了php进程间通讯的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;php单进程单线程处理批量任务太慢了,受不鸟了,但是php不能多线程,最终选择了多进程处理批量任务.php多进程主要使用for进行分裂,然后利用的unix/linux的信号量进行进程间通讯.本例使用…...

godaddy托管wordpress/seo快速优化文章排名

借用人家的插件加上自己处理的兼容&#xff1a; 1.$.support.msie 是判断浏览器兼容方法 jQ9取消了$.support 2.input propertychange&#xff08;ie专属&#xff09; 有兼容问题 可以用keyup事件代替&#xff08;无需兼容&#xff09; 代码&#xff1a;html <!DOCTYPE ht…...

株洲网站建设报价方案/指数函数

这里只是对TCP协议做个简要的介绍。 TCP协议是基于流的可靠的传输层协议&#xff0c;不同于UDP协议&#xff0c;UDP不保证信息传输的可靠性。这就意味着应用程序把数据流交付给TCP后&#xff0c;要依靠TCP保证数据流完整、一致以及按序到达接收方的应用程序上。TCP主要通过差错…...

机构单位网站建设方案/地推团队

PB作为windows下的一个非常便捷的DB开发工具&#xff0c;有着和windows一样的消息触发机制 PB提供了相应event/function触发机制和触发方式&#xff0c;用户可以根据自己的实际需要选用不同方法。 触发机制 trigger/post 使用trigger会即时触发event/function&#xff1b; 使…...

wordpress站点标题看不到/自动秒收录网

今天一早登陆后台&#xff0c;就看到WP3.1的发行版本已经出来了&#xff08;更新日期是12月27日&#xff0c;昨天没留意&#xff0c;呵呵&#xff09; 于是乎&#xff0c;立即下载下来&#xff0c;在自己电脑的XAMPP上跑了下。 前台便捷管理菜单效果如下&#xff1a; .........…...