使用JWT实现登录认证

news/2024/11/25 21:57:07/

一、介绍

1.1、Session、Cookie、Token区别

session:存储再服务端,无法引用与分布式场景,并且需要占用服务端的资源
cookie:存储再客户端,适用于分布式场景,但是存在安全问题,不支持垮域访问
token:存储在localstorage中,更加灵活

1.2、如何实现登录认证

  1. 用户使用账号密码登录成功
  2. 通过JWT生成一串字符串作为Token,返回给前端
  3. 前端每次请求的时候都在请求头中携带上这个Token
  4. 后端每次都使用JWT对该Token进行校验,还原出一些用户信息,以此来判断用户是否登录

1.3、JWT组成

1.3.1、样例

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjpbXSwiaWF0IjoxNjg0ODIzNzIzLCJleHAiOjE2ODQ4NTc1OTksImF1ZCI6IiIsImlzcyI6ImFxaSIsInN1YiI6IiJ9.W306xll5X2lHWL_B0AUZs7nf9e7Zn5QvgoasnviBeaQ

1.3.2、组成

JWT生成的字符串由三个部分组成

第一部分(header:JWT头,该部分只用Base64编码,未加密)
头部由2个属性组成
1、typ:令牌类型,固定设置为JWT
2、alg:加密算法,默认为HS256

第二部分(Payload:有效载荷,该部分只用Base64编码,未加密,避免存放隐私信息)
就是JWT的主体部分
1、issuer:发行者
2、IssuedAt:发布时间
3、expiration:到期时间
4、subject:主题
5、Not Before:生效时间
6、JWT ID:用于标识该 JWT
7、audience:用户

第三部分(Signature:签名,该部分是安全的,无法被解密)
该部分可以设置secret(俗称:加盐)的方式增加该部分的破解难度

1.4、优缺点

1.4.1、优点

  1. 是json格式,跨语言的
  2. 可以利用Payload存储一些非敏感的信息
  3. 不需要存储在服务端,可以用于分布式场景
  4. 一般存储在localStorage中,不存在于Cookie中,避免了一些安全性问题
  5. 便于实现单点登录功能

1.4.2、缺点

  1. 一旦生成就无法修改过期时间,需要搭配缓存来实现过期或者退出效果
  2. 同样Token过期无法进行续签
  3. 不可以在JWT中存储敏感信息

二、使用

2.1、引入POM依赖

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

2.2、编写工具类

package com.xx.utils;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletRequest;
import java.util.Date;/*** @author aqi* DateTime: 2020/11/9 3:27 下午* Description: JWT工具类*/
public class JwtUtils {/*** 设置Token过期时间*/public static final long EXPIRE = 1000 * 60 * 60 * 24;/*** 秘钥*/public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";/*** 生成Token字符串(Token组成:头+荷载+签名)* @param id 用户id* @param nickname 用户名称* @return Token字符串*/public static String getJwtToken(String id, String nickname){return Jwts.builder()// 设置JWT的头信息.setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256")// 设置Token过期时间// 主题.setSubject("guli-user")// 签发时间.setIssuedAt(new Date())// 过期时间.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))// 设置Token主体部分.claim("id", id).claim("nickname", nickname)// 签名算法,秘钥.signWith(SignatureAlgorithm.HS256, APP_SECRET).compact();}/*** 判断token是否存在与有效* @param jwtToken token* @return 判断token是否存在与有效*/public static boolean checkToken(String jwtToken) {if(StringUtils.isEmpty(jwtToken)) {return false;}try {Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 判断token是否存在与有效* @param request request* @return 判断token是否存在与有效*/public static boolean checkToken(HttpServletRequest request) {try {String jwtToken = request.getHeader("token");if(StringUtils.isEmpty(jwtToken)) {return false;}Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 根据token获取会员id* @param request request* @return 会员id*/public static String getMemberIdByJwtToken(HttpServletRequest request) {String jwtToken = request.getHeader("token");if(StringUtils.isEmpty(jwtToken)) {return "";}Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);Claims claims = claimsJws.getBody();return (String)claims.get("id");}}

2.3、登录代码

public String login(UcenterMember member) {//获取登录手机号和密码String mobile = member.getMobile();String password = member.getPassword();//手机号和密码非空判断if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {throw new MyException(20001,"登录失败");}//判断手机号是否正确QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();wrapper.eq("mobile",mobile);UcenterMember mobileMember = baseMapper.selectOne(wrapper);//判断查询对象是否为空if(mobileMember == null) {//没有这个手机号throw new MyException(ResultCode.CODE_20001);}//判断密码if(!DigestUtils.md5DigestAsHex(password.getBytes()).equals(mobileMember.getPassword())) {throw new MyException(ResultCode.CODE_20001);}//判断用户是否禁用if(mobileMember.getIsDisabled()) {throw new MyException(ResultCode.CODE_20001);}//登录成功,生成token字符串return JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());}

2.4、使用JWT做拦截

/*** 根据token获取用户信息*/@GetMapping("/getMemberInfo")public R getMemberInfo(HttpServletRequest request) {//调用jwt工具类的方法。根据request对象获取头信息,返回用户idString memberId = JwtUtils.getMemberIdByJwtToken(request);if (memberId.isEmpty()) {return R.error().data("userInfo", "登陆失效,请重新登录");}//查询数据库根据用户id获取用户信息UcenterMember member = memberService.getById(memberId);return R.success().data("userInfo",member);}

三、续签和过期问题

3.1、思路

将生成的JWT字符串不返回给前端,而是自己生成一个UUID,将该UUID返回给前端,再使用该UUID存储


http://www.ppmy.cn/news/75071.html

相关文章

【多线程】什么是线程死锁?形成条件是什么?如何避免?

文章目录 一、什么是线程死锁二、线程死锁三、形成死锁的四个必要条件是什么四、如何避免线程死锁 一、什么是线程死锁 死锁是指两个或两个以上的进程&#xff08;线程&#xff09;在执行过程中&#xff0c;由于竞争资源或者由于彼此通信而造成的一种阻塞的现象&#xff0c;若…

STL --- 2、容器 set 和multiset

std::set 和 std::multiset 都是 STL 中的关联容器&#xff0c;用于存储一组有序的元素。 1、std::set 和 std::multiset 特点 &#xff08;1&#xff09;std::set 中每个元素的键值都唯一&#xff0c;而 std::multiset 中可以有多个相同的键值。 &#xff08;2&#xff09;s…

Linux搭建MQTT服务器(mosquitto)并使用

零、码仙励志 在路上&#xff0c;寻找一个继续的理由&#xff0c;寻找一个曾经的梦想。 一、Linux搭建MQTT服务器&#xff08;mosquitto&#xff09;并使用 1、安装依赖 yum install gcc-c cmake openssl-devel libuuid-devel c-ares-devel uuid-devel libwebsockets-devel…

HP打印機维护怎么找官方资料

关键词&#xff1a; “Removal and replacement” "part number" 范例 Removal and replacement A7W93 67033 Removal and replacement A7W93-67081 HP PageWide Enterprise, HP PageWide Managed - Removal and replacement: Service fluid container kit | H…

Linux之NetLink学习笔记

1.NetLink机制 NetLink是一种基于应用层跟内核态的通信机制&#xff0c;其特点是一种异步全双工的通信方式&#xff0c;支持内核态主动发起通信的机制。该机制提供了一组特殊的API接口,用户态则通过socket API调用。内核发送的数据再应用层接收后会保存在接收进程socket的缓存…

数据结构与算法之散列表详解

一、散列表概述 散列表&#xff08;Hash Table&#xff09;也叫哈希表&#xff0c;它是一种时间复杂度能够达到接近常数的数据结构&#xff0c;可以用来快速地存储和查找数据。散列表通过哈希函数来将键值对映射为一个索引值&#xff0c;然后通过这个索引值来在数组中访问对应…

【数据库】SQLServer报修表,维修表,反馈表三表连查

大家好&#xff0c;我是雷工&#xff01; 最近参与的一个SCADA项目&#xff0c;客户要求增加设备维保的功能&#xff0c;对设备的报修&#xff0c;维修&#xff0c;反馈过程进行记录查询&#xff0c;进一步提升企业的信息化能力。 该过程的实现是通过创建三个表分别记录报修-维…

【0196】共享内存管理结构(shmem)之创建共享内存分配机制(Shared Memory Allocation)(2)

文章目录 1. 共享内存段(Shared Memory Segment)1.1 设置指向共享内存的基本指针2. 创建共享内存分配机制相关文章: 【0195】共享内存管理结构(shmem)之概念篇(1) 【0