码迷,mamicode.com
首页 > 其他好文 > 详细

JWT原理

时间:2020-12-30 10:48:44      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:malformed   非对称   tno   time   slf4j   方式   key   设置   and   

JWT即json web token,大家先看下面这张图

技术图片

 

 

大家可以观察到,jwt String就是生成后的jwt字符集,其中有两个 "."(注意:jwt校验会对"."个数校验,多或少都会校验失败),被"."分割的就是jwt的三个构成部分,即:header、payload、sign。

 

接下来,给大家讲下jwt的生成规则和校验规则

JWT生成规则:

1、设置加密方式、claims信息(即payload)和signingkey

2、设置加密方式为header,并进行base编码

3、设置claims信息为payload,并进行base64编码

4、对header和payload用"."拼接成jwt,,使用signingkey按照加密方式进行加密,生成sign

5、对Jwt和sign用"."生成最终的jwt

JWT校验规则:

1、设置jwt和signingkey

2、按"."对jwt分成三部分,即:header、payload、sign

3、取第一部分进行base64解码,获取加密方式

4、取第二部分进行base64解码,获取业务参数,即payload

5、使用加密方式和signingkey创建校验器,对header+payload进行加密,并与sign(即第三部分)进行对比

6、取出payload的有效期进行校验,是否过期

7、通过校验,返回claims信息

附:

1、进行base64编码时,会判断是否为android客户端,使用base64的库不一样,这点可以自行看源码。

2、jwt会对生成的base64字符集的特殊符号进行转换,"-"换为"+",“_”换位"/",去掉尾部"="

3、jwt校验时,会判断是否有且只有两个".",否则校验失败

 

原理总结:

1、三部分里的header和payload是独立的,无须signingkey,只需base64解码即可看到payload的信息,所以千万不要在payload里放敏感信息

2、如果是使用jwt作为登录态校验,建议使用对称加密,因为非对称解密效率相对较慢,较多请求下会影响性能

3、因jwt是无状态的,之前见很多同学使用redis进行存储,我不是很明白,这岂不是违反了jwt当初的设计原则

4、保证密钥不要泄露,否则jwt可以被伪造

5、必要情况下,建议使用https

 

另附上个人的代码供大家参考

 

package com.yhc.demo.plugin;

import java.io.UnsupportedEncodingException;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

/**
 * JwtToken工具类
 */
@Configuration
public class JwtService {

	private static final Logger log = LoggerFactory.getLogger(JwtService.class);

	@Value("${jwt.secret:123456}")
	private String secret;
	@Value("${jwt.expiration:60}")
	private Long expiration;

	/**
	 * 生成token
	 * 
	 * @param username
	 * @return token
	 */
	public String generateToken(String username) {

		Claims claims = Jwts.claims();
		claims.setIssuer(username); // jwt发行人
		claims.setIssuedAt(new Date()); // jwt生成时间
		claims.setExpiration(getExp()); // jwt过期时间
		claims.setSubject("auth"); // jwt主题
		claims.setAudience("yhc"); // jwt接受方
		claims.setId("uuid"); // jwt唯一身份标识
		claims.setNotBefore(new Date()); // jwt在此之前不可用

		return generateToken(claims);
	}

	/**
	 * 刷新token
	 * 
	 * @param old token
	 * @return new token
	 */
	public String refreshToken(String token) {
		Claims claims = validToken(token);
		if (claims == null) {
			return null;
		}
		claims.setIssuedAt(new Date());
		claims.setExpiration(getExp());
		return generateToken(claims);
	}

	/**
	 * 根据token获取发行人
	 * 
	 * @param token
	 * @return issuer
	 */
	public String getIssuer(String token) {
		Claims claims = validToken(token);
		return claims.getIssuer();
	}

	/**
	 * 校验jwtToken,如无效,则返回Null,反之,返回负载对象
	 */
	private Claims validToken(String token) {
		Claims claims = getClaimsFromToken(token);
		if (claims != null) {
			Date exp = claims.getExpiration();
			if (exp.before(new Date())) {
				log.warn("# jwtToken已失效:{}", token);
				throw new RuntimeException("invalid token");
			}
		}
		return claims;
	}

	/**
	 * 从token中获取JWT中的负载参数
	 */
	private Claims getClaimsFromToken(String token) {
		Claims claims = null;
		try {
			claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
		} catch (Exception e) {
			log.warn("# jwtToken格式验证失败:{}", token, e);
			throw new RuntimeException("token verification failed");
		}
		return claims;
	}

	/** 根据负载参数生成token */
	private String generateToken(Claims claims) {
		return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
	}

	/** 获取token有效期 */
	private Date getExp() {
		return new Date(System.currentTimeMillis() + expiration * 1000);
	}

	public static void main(String[] args) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
			SignatureException, IllegalArgumentException, UnsupportedEncodingException {

		String username = "yhc";

		Claims claims = Jwts.claims();
		claims.setIssuer(username); // jwt发行人
		claims.setIssuedAt(new Date()); // jwt生成时间
		claims.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)); // jwt过期时间
		claims.setSubject("test"); // jwt主题
		claims.setAudience("yhc"); // jwt接受方
		claims.setId("uuid"); // jwt唯一身份标识
//		claims.setNotBefore(new Date()); // jwt在此之前不可用

		String visitTK = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, "sad12f").compact();
		System.out.println(visitTK);// SystemValue.JWT_HEADER_VALUE_PREFIX +

		Claims claimsDecode = Jwts.parser().setSigningKey("sad12f").parseClaimsJws(visitTK).getBody();
		System.out.println(claimsDecode.getIssuer());

	}
}

 

  

 

 

 

以上纯为个人总结,如有错误,还请指出,谢谢。

JWT原理

标签:malformed   非对称   tno   time   slf4j   方式   key   设置   and   

原文地址:https://www.cnblogs.com/yhc-910/p/14185636.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!