目录
1.Cookie、session、token的区别与联系
- 由于HTTP是无状态协议,客户端每次访问服务端,服务端不会保留客户端的信息,即上一次请求对这一次请求没有任何影响。
- Cookie:浏览器向服务器发送请求,服务器会set-cookie设置Cookie的值(key,value),服务器发送给浏览器,浏览器保存Cookie的值,之后客户端每次访问浏览器,都会带上这个Cookie值,打开浏览器可以查看保存了哪些Cookie的值。(不安全)
- Session:浏览器向服务器发送请求,服务器验证用户名和密码是对的,会创建一个SessionId和会话结束时间,然后将SessionId放入Cookie中,会话结束时间就是Cookie的有效期。之后每次访问都会携带带有SessionId的Cookie值,直到有效期结束,就需要再次输入用户名和密码
- 随着用户体积的增大,服务器端需要保存大量的Session,对内存的消耗大。如果搭建的是分布式集群项目,又存在Session共享的问题。因此推出了JWT
- JWT:生成于服务端,保存于客户端。
2.传统的Session认证
3.基于JWT认证
4.JWT的结构
- 标头(header)
- 有效载荷(payload)
- 签名(signature)
标头
包含两个部分:令牌的类型和所使用的签名算法.标头使用的是Base64编码,注意Base64编码不是一种加密的过程,可以被解码为初始值。
{
"typ":"JWT",
"alg":"HS256"
}
有效载荷
有效载荷存放的是用户的数据信息,通过Base64进行加密,不要在Payload中存放重要的值。
签名
签名是将编码后的(标头、有效载荷和我们提供的一个密钥)和我们Header中指定的签名算法进行编码。
比如客户端传入 Header(A)、Payload(B)、Signature(C),我们需要通过A、B和密钥通过Base64编码后的值和C进行对比,如果值相同则正确。
第一个JWT程序
@Test
void contextLoads() {
Map<String, Object> map = new HashMap<String,Object>();
map.put("typ","JWT");
map.put("alg","HS256");
Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND,50);
String token= JWT.create()
.withHeader(map)
.withClaim("username", "lele")
.withClaim("age", 17)
.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256("uadisjdlkas**&&^&"));
System.out.println(token);
}
@Test
void jiemi(){
//获取到签名
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("uadisjdlkas**&&^&")).build();
//进行验证
DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTkxNTQyNjMsImFnZSI6MTcsInVzZXJuYW1lIjoibGVsZSJ9.SVjXtp35e3AioWzk7TADKDeGGWTSHOmtJ2pGLCBoiuc");
//允许放多个
//得到
System.out.println(verify.getClaim("username").asString());
System.out.println(verify.getClaim("age").asInt());
System.out.println(verify.getExpiresAt());
}
常见的异常信息
JWTUtils工具类(直接使用)
package com.lele.utils;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.*;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.SecretKey;
/**
* @author:Tlimited
*
*/
public class JWTUtils {
private static final long EXPIRE = 60 * 1000; //过期时间
public static final String key = "ajsdaklsdjalskdasj";//密钥,动态生成的密钥
/**
* 生成token
*
* @param claims 要传送消息map
* @return
*/
public static String generate(Map<String,Object> claims) {
Date nowDate = new Date();
//过期时间,设定为一分钟
Date expireDate = new Date(System.currentTimeMillis() + EXPIRE);
//头部信息,可有可无
Map<String, Object> header = new HashMap<>(2);
header.put("typ", "jwt");
//更强的密钥,JDK11起才能用
// KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
// PrivateKey key1 = keyPair.getPrivate(); // 私钥
//PublicKey key2 = keyPair.getPublic(); //公钥
return Jwts.builder().setHeader(header)
// .setSubject("weimi")//主题
// .setIssuer("weimi") //发送方
.setClaims(claims) //自定义claims
.setIssuedAt(nowDate)//当前时间
.setExpiration(expireDate) //过期时间
.signWith(SignatureAlgorithm.HS256,key)//签名算法和key
.compact();
}
/**
* 生成token
* @param header 传入头部信息map
* @param claims 要传送消息map
* @return
*/
public static String generate( Map<String, Object> header,Map<String,Object> claims) {
Date nowDate = new Date();
//过期时间,设定为一分钟
Date expireDate = new Date(System.currentTimeMillis() + EXPIRE);
return Jwts.builder().setHeader(header)
// .setSubject("weimi")//主题
// .setIssuer("weimi") //发送方
.setClaims(claims) //自定义claims
.setIssuedAt(nowDate)//当前时间
.setExpiration(expireDate) //过期时间
.signWith(SignatureAlgorithm.HS256,key)//签名算法和key
.compact();
}
/**
* 校验是不是jwt签名
* @param token
* @return
*/
public static boolean isSigned(String token){
return Jwts.parser()
.setSigningKey(key)
.isSigned(token);
}
/**
* 校验签名是否正确
* @param token
* @return
*/
public static boolean verify(String token){
try {
Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token);
return true;
}catch (JwtException e){
System.out.println(e.getMessage());
return false;
}
}
/**
* 获取payload 部分内容(即要传的信息)
* 使用方法:如获取userId:getClaim(token).get("userId");
* @param token
* @return
*/
public static Claims getClaim(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
e.printStackTrace();
}
return claims;
}
/**
* 获取头部信息map
* 使用方法 : getHeader(token).get("alg");
* @param token
* @return
*/
public static JwsHeader getHeader(String token) {
JwsHeader header = null;
try {
header = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token)
.getHeader();
} catch (Exception e) {
e.printStackTrace();
}
return header;
}
/**
* 获取jwt发布时间
*/
public static Date getIssuedAt(String token) {
return getClaim(token).getIssuedAt();
}
/**
* 获取jwt失效时间
*/
public static Date getExpiration(String token) {
return getClaim(token).getExpiration();
}
/**
* 验证token是否失效
*
* @param token
* @return true:过期 false:没过期
*/
public static boolean isExpired(String token) {
try {
final Date expiration = getExpiration(token);
return expiration.before(new Date());
} catch (ExpiredJwtException expiredJwtException) {
return true;
}
}
/**
* 直接Base64解密获取header内容
* @param token
* @return
*/
public static String getHeaderByBase64(String token){
String header = null;
if (isSigned(token)){
try {
byte[] header_byte = Base64.getDecoder().decode(token.split("\\.")[0]);
header = new String(header_byte);
}catch (Exception e){
e.printStackTrace();
return null;
}
}
return header;
}
/**
* 直接Base64解密获取payload内容
* @param token
* @return
*/
public static String getPayloadByBase64(String token){
String payload = null;
if (isSigned(token)) {
try {
byte[] payload_byte = Base64.getDecoder().decode(token.split("\\.")[1]);
payload = new String(payload_byte);
}catch (Exception e){
e.printStackTrace();
return null;
}
}
return payload;
}
public static void main(String[] args) {
//用户自定义信息claims
Map<String,Object> map = new HashMap<>();
map.put("userId","test122");
String token = generate(map);
System.out.println(token);
System.out.println("claim:" + getClaim(token).get("userId"));
System.out.println("header:" + getHeader(token));
// System.out.println(getIssuedAt(token));
Claims claims=getClaim(token);
// System.out.println(getHeaderByBase64(token));
System.out.println(getPayloadByBase64(token));
SimpleDateFormat sdf=new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
System.out.println("签发时间:"+sdf.format(claims.getIssuedAt()));
System.out.println("过期时间:"+sdf.format(claims.getExpiration()));
System.out.println("当前时间:"+sdf.format(new Date()) );
}
}