学习目标
1.加密
2.签名
起源
session变jwt token?
session的意义就是解决http的无状态请求,标志出每个请求对应的在线状态的用户。
问题是什么?我们有的服务是需要认证授权才能访问的,也就是用户在我们这边注册过,是我们服务的用户我们才服务。但是web每次请求
1.为什么http要是无状态的?
答:因为之前的http是有状态的,那么服务器就需要维持保存浏览器的状态,而且不知道浏览器的状态是什么时候断开,也就是什么时候关闭浏览器,这回造成服务器不断维持客户端信息等等,造成资源浪费,性能下降。
所以设计http无状态,浏览器随时访问,而且不保存当前浏览器的会话的状态信息,充分利用资源和性能。 也就是每次请求都当成一个未注册的新用户对待。 此时无状态的http抵达服务器的时候,服务器并不知道是那个会话状态的用户发送的。
2.所以,怎么判断是哪个用户发来的呢?
答:因为每次请求都当初未注册的新用户,顾我们可以让每次http都携带上username和password,相当于 ”用户每次想要我们服务她的时候,都需要说一句’我是会员’“,而我们会微笑的面对他说”好的,我查查我们的会员本先哈“,之后如果是会员 ”你好,我来服务你了会员哥哥“。
总结:
她每次声称自己是会员,我每次查会员表。这就是http的简化版工作理解。
jwt能做什么? 加密和签名
#1.授权
一但用户登录,每个后续请求将包括jwt,从而允许用户访问该令牌允许访问的路由,服务与资源。
#2.信息交换 (签名可以防伪造)
安全的传输信息。因为可以对jwt进行签名(例如,公钥和密钥),所以你可以确保发件人是他们的本人(防伪造)。而且签名可以通过计算,判断内容是否进行了修改。
jwt的结构
x.y.z
x y是base64编码,转成字符串,谁都可以解码,并不是加密原则
疑问:那不是不安全吗?拦截后,使用你的token进行操作
jwt 生成和验签解码
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Calendar;
@SpringBootTest
class DemoApplicationTests {
/***
* 生成token
*/
@Test
void contextLoads() {
//1.不用设置头部,使用默认base64
//2.设置载荷
// 2.1用户信息(名称,角色,权限)
// 2.2token过期时间
//3.设置密钥 sign+密钥
Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND,2000);
System.out.println(instance.getTime());
String token = JWT.create()
// .withHeader()头部不用设置,使用默认base64算法
.withClaim("username", "小黑")
.withClaim("userId", 21)
.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256("!@E#DFKJDKG"));
System.out.println(token);
}
/***
* 验签
*/
@Test
void vertify(){
//1.创建验签器,需要 ‘加密算法和密钥’
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("!@E#DFKJDKG")).build();
//2.解码对象解码获取结构中数据
DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NDg4Njc1MjcsInVzZXJJZCI6MjEsInVzZXJuYW1lIjoi5bCP6buRIn0.kgvAtcu7_WOGw_mSCmUqmBOyD8OaUUOAJWz5JHqvIcM");
//3.通过解码对象获取jwt三大结构值
//3.1 通过getClaims获取载荷信息值
System.out.println(verify.getClaims().get("username").asString());
System.out.println(verify.getClaims().get("userId").asInt());
System.out.println(verify.getClaim("username").asString());
System.out.println(verify.getHeader());
System.out.println(verify.getSignature());
}
}
jwtUtil
package com.example.shirodemo.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
private static final Logger log = LoggerFactory.getLogger(JwtUtil.class);
/**
* 过期时间5分钟
*/
private static final long EXPIRE_TIME = 5 * 60 * 1000;
/**
* 生成签名,5min后过期
*
* @param username 用户名
* @param secret 用户的密码
* @return 加密的token
*/
public static String sign(Map<String,String> map, String secret) {
//两种时间过期方式
// 第一种:
// Calendar instance = Calendar.getInstance();
// instance.add(Calendar.SECOND,2000);
// 第二种
// Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTCreator.Builder builder = JWT.create();
map.forEach((k,v)->{
builder.withClaim(k,v);
});
String token = builder.withExpiresAt(date).sign(algorithm);
return token;
}
/**
* 校验token是否正确
*
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String username, String secret) {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
try {
DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException e) {
return false;
}
return true;
}
/**
* 获得token中的信息:无需secret解密也能获得
*/
public static String getUsername(String token) {
return getClaim(token, "username");
}
public static String getClaim(String token,String username){
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim(username).asString();
} catch (JWTDecodeException e) {
return null;
}
}
}
报错
无法序列化没有参数的对象,因为json没有{}对象,没有纯花括号的对象。