JWT进行跨域身份验证

一、引言

在这里插入图片描述
上面这张图是传统的用户身份认证流程,一般过程如下:

1、用户向服务器发送用户名和密码;
2、 验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中;
3、 服务器向用户返回session_id,session信息都会写入到用户的Cookie;
4、 用户的每个后续请求都将通过在Cookie中取出session_id传给服务器;
5、 服务器收到session_id并对比之前保存的数据,确认用户的身份;

对于分布式架构设计,这种传统模式是存在问题的,它不能有效的支持横向扩展。

二、JWT

1、什么是JWT?

官网介绍:JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地将信息作为JSON对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公用/专用密钥对对JWT进行签名。

尽管可以对JWT进行加密以在各方之间提供保密性,但我们将重点关注已签名的令牌。签名的令牌可以验证其中包含的声明的完整性,而加密的令牌则将这些声明隐藏在其他方的面前。当使用公钥/私钥对对令牌进行签名时,签名还证明只有持有私钥的一方才是对其进行签名的一方。

简单总结一句话:JWT是一个JSON信息传输的开放标准,它可以使用密钥对信息进行数字签名,以确保信息是可验证和可信任的。

2、JWT的结构

JWT由三部分构成:header(头部)、payload(载荷)和signature(签名)。 这三部分紧密排列,并由“.“分隔,因此,JWT通常如下所示。

xxxxx.yyyyy.zzzzz

其中,第一部分为 header

header通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256或RSA等等。例如:

 {
 	"alg": "HS256",
   	"typ": "JWT" 
 }

alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256)typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,会使用Base64 URL算法将上面JSON对象转换为字符串保存,也就是xxxxx.yyyyy.zzzzz中的xxxxxx

其中,第二部分为 Payload

Payload也叫有效载荷部分,是JWT的主体内容部分,它也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择

iss: 该JWT的签发者
exp(expires): 什么时候过期,这里是一个Unix时间戳
sub: 该JWT所面向的用户
aud: 接收该JWT的一方
nbf:在此之前不可用
iat(issued at): 在什么时候签发的
jti:JWT ID用于标识该JWT

除以上默认字段外,我们还可以自定义私有字段,如下例:

{
	"sub": "1234567890",
	"name": "Helen",
	"admin": true
}

举个例子:

{
  "iss": "www.baidu.com",
  "sub": "you",
  "aud": "me",
  "name": "456",
  "admin": true,
  "iat": 1584091337,
  "exp": 1784091337,
}

这部分同样会被Base64编码,然后形成JWT的第二部分,也就是xxxxx.yyyyy.zzzzz中的yyyyyy。

其中,第三部分为 Signature

这是JWT的第三部分,叫做签名,此部分用于防止JWT内容被篡改。将上面的两个编码后的字符串都用英文句号“.”连接在一起(头部在前),就形成了

xxxxxx.yyyyyy

然后再使用header中声明签名算法进行签名,以确保数据不会被篡改,如果要使用HMAC SHA256算法,则将通过以下方式创建签名:

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

当然,在加密的时候,我们还需要提供一个密钥(secret),该密钥仅仅为保存在服务器中,并且不能向用户公开,这样就形成了JWT的第三部分,也就是xxxxx.yyyyy.zzzzz中的zzzzzz

最后,我们把这三个部分拼在一起,就形成了一个完整的JWT对象。

在这里插入图片描述

3、Base64URL算法

如前所述,JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类
似,但稍有差别。

作为令牌的JWT可以放在URL中(例如api.example/?token=xxx)。 Base64中用的三个字符 是"+""/""=",由于这三个字符在URL中有特殊含义,因此Base64URL中对他们做了替换:"=“去掉,”+“用”-“替换,”/“用”_"替换,这就是Base64URL算法。

4、JWT的原则

JWT的原则是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,之后,当用户与服务器通信时,客户在请求中携带这个JSON对象。服务器仅依赖于这个JSON对象来标识用户,为了防止用户篡改数据,服务器将在生成对象时添加签名。服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。

5、JWT的用法

客户端接收服务器返回的JWT,将其存储在CookielocalStorage中。此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。

6、JWT问题和趋势

(1)、JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。
(2)、生产的token可以包含基本信息,比如id、用户昵称、头像等信息,避免再次查库
(3)、存储在客户端,不占用服务端的内存资源
(4)、JWT默认不加密,但可以加密。生成原始令牌后,可以再次对其进行加密。
(5)、当JWT未加密时,一些私密数据无法通过JWT传输。
(6)、JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权 限。也就是说,一旦JWT签发,在有效期内将会一直有效。
(7)、JWT本身包含认证信息,token是经过base64编码,所以可以解码,因此token加密前的对象 不应该包含敏感信息,一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用, JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。
(8)、为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进 行传输。

7、JWT认证流程

在这里插入图片描述

三、整合代码

1、在需要的模块中添加jwt工具依赖
<dependencies> 
	<!-- JWT-->
	<dependency> 
		<groupId>io.jsonwebtoken</groupId>
		<artifactId>jjwt</artifactId> 
	</dependency> 
</dependencies>
2、创建JWT工具类

public class JwtUtils {
	
	public static final long EXPIRE = 1000 * 60 * 60 * 24; //token过期时间
	public static final String APP_SECRET = 
					"ukc8BDbRigUDaY6pZFfWus2jZWLPHO"; //秘钥
	
	//生成token字符串的方法
	public static String getJwtToken(String id, String nickname){
	
		String JwtToken = Jwts.builder()
		.setHeaderParam("typ", "JWT")
		.setHeaderParam("alg", "HS256")
		
		.setSubject("common-user")
		.setIssuedAt(new Date())
		.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
		
		.claim("id", id) //设置token主体部分 ,存储用户信息
		.claim("nickname", nickname)
		.signWith(SignatureAlgorithm.HS256, APP_SECRET)
		.compact();
		
		return JwtToken;
	}
	
	/**
	* 判断token是否存在与有效
	* @param jwtToken
	* @return
	*/
	public static boolean checkToken(String jwtToken) {
		if(StringUtils.isEmpty(jwtToken)) return false;
		try {
			Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	* 判断token是否存在与有效
	* @param request
	* @return
	*/
	public static boolean checkToken(HttpServletRequest request) {
		try {
			String jwtToken = request.getHeader("token");
			if(StringUtils.isEmpty(jwtToken)) return false;
			Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	* 根据token获取会员id
	* @param request
	* @return
	*/
	public static String getMemberIdByJwtToken(HttpServletRequest request) {
		String jwtToken = request.getHeader("token");
		if(StringUtils.isEmpty(jwtToken)) return "";
		Jws<Claims> claimsJws = Jwts.parser()
									.setSigningKey(APP_SECRET)
									.parseClaimsJws(jwtToken);
		Claims claims = claimsJws.getBody();
		return (String)claims.get("id");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

止步前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值