JWT入门级教程
传统的session实现方式
1.随着平台用户增多,存储在服务器上面的session的越多,会占用服务器资源,用户量大,会导致频繁出发GC(可以设置过期时间)
2.限制了扩展性,session是保存在服务器上面的,那对于分布式服务,就意味着他只能从这台机器中获取。(当然你可以用redis存储session,但是会导致服务器开销,用token的话 可以完美解决这个问题)
3.session是基于cookie来确定用户的身份的,但cookie容易被截取,跨站请求的伪造攻击(攻击者盗用了你的身份,以你的名义发送恶意请求。)
session实现原理
用户进行登录请求,服务端会返回一个jessionID给客户端,客户端保存这个jesseionID,然后下次请求的时候,把这个jessionID放在请求头中,服务端就会进行验证,就知道这个人是谁。
token的认证机制
用户请求登录的时候,他会生成一个token,然后返回给客户端,客户端将这个token存储在浏览器中,然后下次访问的时候,在头部加入这个token就行了,服务端不需要存储任何东西,你只要把token传递过来了,后端就会进行解密,分析,不会占用服务器资源。
JWT构成
head
playload
就是负载 用来存放用户一些不敏感信息 例如用户id 用户名
你也可以在负荷添加一些其他的信息
signature
JWT实战
pom.xml
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.7.0</version>
</dependency>
完整案例
package com.cz.testmail.security;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.*;
import com.cz.testmail.Vo.User;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class TokenUtils {
// 1:定义加密的盐信息 enc
private static final String KEY = "nHnds.sg47hskgpl";
// 2:发行者
private static final String ISSUSER = "cz";
// 3:定义token的过期时间 秒 分 小时 天 月
public static final Long TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 30L;
//加密
private static Algorithm algorithm = Algorithm.HMAC256(KEY);
/* 1 :header --确定算法和类型
2 :payload --确定要加密的数据
3 :sign --把header和payload + 盐 进行同一组合生成一个token
有状态:服务端来维护你的数据信息,消耗的服务器资源
无状态:服务段不维护你数据信息,不会消耗服务器资源*/
public static String getToken(User user) {
// token签发的时间
Date now = new Date();
// 1: 开始创建和生成token token1 == token2
String token = JWT.create()
.withIssuer(ISSUSER) // 签发者
.withIssuedAt(now)// token签发的时间
.withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME)) // 设定jwt的服务器过期时间
.withClaim("username", user.getUserName())
.withClaim("userid", user.getUserId())
.sign(algorithm);
System.out.println(user.getUserId());
return token;
}
/**
* 验证token
*
* @param token
* @param username
* @return
*/
public static JSONObject verify(String token, String username) {
JSONObject jsonObject=new JSONObject();
try {
// 1: 定义算法
Algorithm algorithm = Algorithm.HMAC256(KEY);
// 2: 进行校验 你这里就是根据用户名来效验 它这里会自动进行反解密 把token的东西
// 反解密出来 跟username进行一个比较 这里定义的username需要跟你上面携带的claim一直
JWTVerifier jwtVerifier = JWT.require(algorithm).withIssuer(ISSUSER)
.withClaim("username", username).build();
jwtVerifier.verify(token);// 如果校验成功,不做任何动作 ,如果校验失败就抛出异常
jsonObject.put("code",200);
return jsonObject;
} catch (Exception ex) {
System.out.println(ex);
System.out.println("抛异常");
jsonObject.put("code",500);
return jsonObject;
}
}
// 直接根据token进行效验 不判断用户是谁
public static JSONObject verifyToken(String token){
JSONObject jsonObject1=new JSONObject();
try {
// 1: 定义算法
Algorithm algorithm = Algorithm.HMAC256(KEY);
JWTVerifier jwtVerifier = JWT.require(algorithm).withIssuer(ISSUSER).build();
jwtVerifier.verify(token);// 如果校验成功,不做任何动作 ,如果校验失败就抛出异常
jsonObject1.put("code",200);
return jsonObject1;
} catch (Exception ex) {
System.out.println(ex);
System.out.println("抛异常");
jsonObject1.put("code",500);
return jsonObject1;
}
}
/**
** 获取token中的用户信息
*@Author cz
*@Date 2021/8/19 14:27
*@Param
*/
public static String getUserInfoToken(String token){
JWTVerifier jwtVerifier = JWT.require(algorithm).withIssuer(ISSUSER).build();
DecodedJWT verify = jwtVerifier.verify(token);// 如果校验成功,不做任何动作 ,如果校验失败就抛出异常
Claim claim1 = verify.getClaim("userid");
System.out.println(claim1.asInt());
Claim claim = verify.getClaim("username");
System.out.println(claim.asString()+"username");
return "根据你自己需求返回";
}
public static void main(String[] args) {
User user = new User();
user.setUserName("test");
user.setUserId(1);
String token = getToken(user);
System.out.println(TokenUtils.verifyToken(token));
System.out.println("=========");
System.out.println(getUserInfoToken(token));
}
}
拦截器
前端将token放入header中,拦截器进行解析,正确即往下执行。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getRequestURI().contains("login")) {
return true;
}
System.out.println("开始拦截");
//拿到请求链接中包含的数据
String userName= getParam(request,"userName");
//拿到请求头中的token
String token = getParam(request,"Authorization");
//会检验token是否过期
TokenUtils.verify(token,userName);
System.out.println("用户验证成功");
return true;
}
public static String getParam(HttpServletRequest request, String filedName) {
//1 :在参数里面区获取对应filedName的值
String param = request.getParameter(filedName);
// 2:如果不存在
if (StringUtils.isEmpty(param)) {
// 3: 就请求头区获取对应filedName的值
param = request.getHeader(filedName);
}
return param;
}