-
使用网关在系统中比较适合进行权限的校验
-
当有了网关之后,用户所有的请求都会经过当前的网关,然后由网关将用户的请求转发到具体的某一个服务上。
现在网关相当于后端服务统一的入口,所以可以方便的在网关系统中来进行相关服务权限的校验。
所谓的鉴权就是来鉴定你当前有没有访问的权限。如果有访问权限,网关就会把当前的请求给到具体的某一个服务上,如果没有访问权限,那么你本次的请求网关会直接拒绝掉
采用JWT令牌的方式来实现鉴权校验
JWT
全称 JSON Web Token可以通过它在当前客户端与当前服务器端来进行安全可靠的消息传递。
一个JWT令牌实际上就是一个字符串,内部有三部分组成:头部、载荷、签名
头部(Header)描述关于JWT的最基本的信息
例如:当前的类型、签名所使用的算法等。这也可以表示成一个JSON对象
{"typ":"JWT","alg":"HS256"}
解释:
- tye:声明当前我们要用的类型为JWT
- alg:声明当前JWT要使用的签名算法为HS256(对称加密算法)
将JWT头部的JOSN对象({“typ”:“JWT”,“alg”:“HS256”})进行BASE64编码 (在线BASE64编码解码:https://tool.oschina.net/encrypt?type=3)
编码后的字符串为:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
载荷(payload)表示当前JWT它里边的内容(存放有效信息的地方)
JWT在使用的时候其实它本身是非常灵活的,可以非常方便的向当前的JWT令牌中来存放相关的信息。
根据当前的业务,想在JWT令牌中存放什么就可以存放什么。
这些自定义的信息它都放在JWT的第二部分(载荷),也就是当前载荷(payload)这部分。
同样它也可以表示成一个JOSN对象。
{"sub":"1234567890","name":"John Doe","admin":true}
然后将其进行BASE64编码,得到JWT第二部分(载荷)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
签证(signature) JWT的第三部分:是一个签证信息,由三部分组成
第一部分:header(BASE64编码后的内容)
第二部分:playload(BASE64编码后的内容)
第三部分:secret 当前秘钥。放的就是我当前一些隐秘性的信息,并且这个信息是保存在当前服务端的(客户端是不知道的)
想:
- BASE64提供了编码和解码的API,那现在JWT要来进行数据传递,通过BASE64编码完成后,它能保证数据的安全性吗?
解决:保证不了的,因为你通过BASE64编码对应我们当然也可以通过BASE64进行解码(数据全都暴露出去了),但是JWT知道了这一点,所以JWT加了第三部分secret(当前秘钥)
得到第三部分编码的流程:
最后得到的签证(signature)内容
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
最终的JWT
- 将这三部分(头部、载荷、签名)用.(点)连成一个完整的字符串,构成最终的JWT
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
JJWT签发与验证token
- JJWT:JWT的Java客户端,使用JWT的java客户端来完成当前JWT令牌的生产及验证
创建token
依赖
<dependency><!--JWT的Java客户端的依赖-->
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
生成JWT令牌
package cn.cdw.jwt;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class TestJWT {
public static void main(String[] args) {
//生成JWT令牌
JwtBuilder builder = Jwts.builder();
builder.setId("66");//设置JWT的唯一编号
builder.setSubject("JWT的主题");//设置JWT的主题
builder.setIssuedAt(new Date());//设置JWT令牌的签发日期
builder.signWith(SignatureAlgorithm.HS256, "chen");//设置JWT的签名算法以及JWT的秘钥
//根据上面的JwtBuilder对象生成JWT令牌
String jwtToken = builder.compact();
System.out.println("jwt的令牌: " + jwtToken);
}
注意:在写JWT秘钥的时候,如果字符串只有三个字符会包异常:Exception in thread “main” java.lang.IllegalArgumentException: secret key byte array cannot be null or empty.
解析token
package cn.cdw.jwt.test;
import io.jsonwebtoken.*;
public class TestJWT {
public static void main(String[] args) {
/*
解析JWT令牌,得到其内部数据
*/
//jwt的令牌: eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NiIsInN1YiI6IkpXVOeahOS4u-mimCIsImlhdCI6MTU4MDczNTA2M30.-xyIMEzbLO4IOxgn3Sj8IEiCXkUg7tIHXn_MhnRLPmU
//jwt秘钥:chen
String compactJwt="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NiIsInN1YiI6IkpXVOeahOS4u-mimCIsImlhdCI6MTU4MDczNTA2M30.-xyIMEzbLO4IOxgn3Sj8IEiCXkUg7tIHXn_MhnRLPmU";
//parser() 表示解析
JwtParser parser = Jwts.parser();
//设置解析jwt令牌的jwt秘钥(必须要一致对应),如果不一致就会导致当前JWT令牌无法解析
JwtParser signingKey = parser.setSigningKey("chen");
//设置要解析的jwt令牌
Jws<Claims> claimsJws = signingKey.parseClaimsJws(compactJwt);
//获取解析结果
Header header = claimsJws.getHeader();
Claims body = claimsJws.getBody();
String signature = claimsJws.getSignature();
System.out.println("头部: "+header);
System.out.println("载荷: "+body);
System.out.println("签证: "+signature);
}
}
- jti:JWT令牌的唯一编号
- sub:JWT的主题
- iat:JWT令牌的签发日期
测试
可以试着将Token(jwt令牌)篡改(异常:JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.)
或者秘钥篡改(异常:com.fasterxml.jackson.core.JsonParseException: Unrecognized token ‘y’: was expecting (‘true’, ‘false’ or ‘null’))
所以解析Token也就是验证token