文章目录
- 一、Cookie-Session认证
- 校验流程
- 缺点
- 二、Token
- 校验流程
- 缺点
- 三、JWT
- 校验流程
- 四、JWT令牌的实践使用
- JWT组成
- 引入依赖
- 生成令牌
前言
在讲解这个问题之前,我们要先搞清楚基本的用户登录流流程。
- 用户在web登录页面填写登录信息
- 前端发送登录信息到后端
- 后端接收登录信息
- 后端进行校验
- 校验成功,返回成功结果
在本文中会讲解三种常见的登录校验流程
1.Cookie-Session认证
2.
一、Cookie-Session认证
校验流程
- 用户在web端输入登录信息
- 后端拿到信息后进行校验
- 后端校验成功后生成SessionId(这个SessionId关联着用户信息)返回给web
- 前端拿到SessionId后就会保存到Cookie中
- 在后续的操作中,每次用户访问就会带着着这个Cookie
- 后端在后续接收到前端的请求时,就会解析这个SessinId,查询到SessionId关联到的用户信息
- 解析成功后,就会根据用户的请求返回响应
流程图如下
缺点
- Cookie-Session 这种形式只存在于web端
我们目前更多的使用是在手机端,并不是通过web网页的形式。 - Cookie不能跨域
- 不适用于集群环境
二、Token
校验流程
- 用户在web端输入登录信息
- 后端拿到信息后进行校验
- 后端校验成功后生成Toekn(这个Toekn可能是根据用户id生成的,Token关联着用户表)返回给web
- 前端拿到后会将Token存储下来
- 在后续的操作中,每次用户访问就会带着着这个Token
- 后端在后续接收到前端的请求时,就会解析这个Token,查询到关联到的用户信息
- 解析成功后,就会根据用户的请求返回响应
流程图如下
缺点
- 数据量大,对于内存来说,压力巨大、
后端在解决集群环境的问题时,将Toekn存储到Redis中,一旦数据量起来,就会对内存造成巨大压力
三、JWT
校验流程
- 用户在web端输入登录信息
- 后端拿到信息后进行校验
- 后端校验成功后生成JWT令牌,返回给web
- 前端拿到后会将JWT令牌存储下来
- 在后续的操作中,每次用户访问就会带着着这个JWT令牌
- 后端在后续接收到前端的请求时,就会解析这个JWT令牌,查询到关联到的用户信息
- 解析成功后,就会根据用户的请求返回响应
流程图如下
四、JWT令牌的实践使用
JWT组成
JWT由三部分组成, 每部分中间使⽤点 (.) 分隔,⽐如:aaaaa.bbbbb.cccc
- Header(头部) 头部包括令牌的类型(即JWT)及使用的哈希算法(如SHA256)
- Payload(负载) 负载部分是存放有效信息的地⽅, ⾥⾯是⼀些⾃定义内容
- Signature(签名) 此部分⽤于防⽌jwt内容被篡改,确保安全性
JWT之所以安全,就是因为这个签名,JWT当中任何一个字符被篡改,整个令牌都会失效
引入依赖
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --><version>0.11.5</version><scope>runtime</scope></dependency>
生成令牌
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParserBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;public class JWTUtil {private static final Logger logger = LoggerFactory.getLogger(JWTUtil.class);/*** 密钥:Base64编码的密钥*/private static final String SECRET = "SDKltwTl3SiWX62dQiSHblEB6O03FG9/vEaivFu6c6g=";/*** 生成安全密钥:将一个Base64编码的密钥解码并创建一个HMAC SHA密钥。*/private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(Decoders.BASE64.decode(SECRET));/*** 过期时间(单位: 毫秒)*/private static final long EXPIRATION = 60*60*1000;/*** 生成密钥** @param claim {"id": 12, "name":"张山"}* @return*/public static String genJwt(Map<String, Object> claim){//签名算法String jwt = Jwts.builder().setClaims(claim) // 自定义内容(载荷).setIssuedAt(new Date()) // 设置签发时间.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) // 设置过期时间.signWith(SECRET_KEY) // 签名算法.compact();return jwt;}/*** 验证密钥*/public static Claims parseJWT(String jwt){if (!StringUtils.hasLength(jwt)){return null;}// 创建解析器, 设置签名密钥JwtParserBuilder jwtParserBuilder = Jwts.parserBuilder().setSigningKey(SECRET_KEY);Claims claims = null;try {//解析tokenclaims = jwtParserBuilder.build().parseClaimsJws(jwt).getBody();}catch (Exception e){// 签名验证失败logger.error("解析令牌错误,jwt:{}", jwt, e);}return claims;}/*** 从token中获取用户ID*/public static Integer getUserIdFromToken(String jwtToken) {Claims claims = JWTUtil.parseJWT(jwtToken);if (claims != null) {Map<String, Object> userInfo = new HashMap<>(claims);return (Integer) userInfo.get("userId");}return null;}
}