在微服务框架中,用户信息是个很头疼的问题。首先为了实现单点登录,用户在一个系统登录后,就无需在其他系统再进行登录,那么用户信息就需要在第一次登录后进行存储到cookie,那么当我们在cookie中获取用户信息时,该如何验证用户信息没有被篡改?其次用户信息都是敏感数据,如果明文展示势必会造成隐私泄露。为了解决这些问题,就有必要学习JWT令牌。
一、JWT介绍
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。JWT一共有三部分组成,分别是:头部、载荷、签证。
1.头部(Header)
头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。它是一个JSON字符串,如下例子:
{
"typ":"JWT",//类型
"alg":"HS256"//算法
}
2.载荷(playload)
载荷就是存放用户信息、和有效数据的地方。它一共有三个部分组成
(1)标准中注册的声明
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
(2)公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密。
(3)私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息。这个指的自定义的信息,这些信息跟JWT标准规定的信息区别在于:JWT规定的信息,JWT的接收方在拿到JWT之后,都知道怎么对这些标准的信息进行验证(还不知道是否能够验证);而私有的信息不会验证,除非明确告诉接收方要对这些信息进行验证以及规则才行。如下例子:
{
"iss":"xiaoming", //标准声明
"name":"xiaowang", //私有声明
"age":10//私有声明
}
3.签证
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
Header通过Base64加密的字符串 + playload通过Base64加密的字符串 + 秘钥
完整的字符串如下:
eyJhbGciJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZ6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.VA95OrM7E2cBab3MHrcEfxjoYZgeFONFh7HgQ
需要注意的是,秘钥是保存在服务端的,令牌签发也是在服务端,因此秘钥只能在服务器中,不能泄露出去,否则客户端能自己进行签发令牌。
最重要的是签证的作用就是用来验证数据是否被进行了修改,根据签证生成规则可以看到,如果载荷中的信息被修改了,那么生成的字符串和签发的令牌一定是不相同的。
二、JWT的使用
1.引入maven
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
2.令牌签发、解析实例
public static void main(String[] args) {
String jwt=createJwt();
System.out.println("签发的令牌:"+jwt);
Claims claims=parseJwt(jwt);
String jsonStr= JSON.toJSONString(claims);
System.out.println(jsonStr);
}
/**
* 签发令牌
* @return
*/
public static String createJwt(){
JwtBuilder builder= Jwts.builder()
.setId(UUID.randomUUID().toString().replace("-","")) //设置唯一编号
.setSubject("测试主题") //设置发送的主题
.setExpiration(new Date()) //设置过期时间
.setIssuedAt(new Date()) //设置签发日期
.signWith(SignatureAlgorithm.HS256,"abc");//设置签名 使用HS256算法,abc为私钥
//设置用户信息
Map<String,Object> map=new HashMap<>();
map.put("name","小明");
map.put("age",25);
builder.addClaims(map);
//构建 并返回一个字符串
return builder.compact() ;
}
/**
* 解析令牌
* @param jwt
* @return
*/
public static Claims parseJwt(String jwt){
Claims claims = Jwts.parser().
setSigningKey("abc").//设置私钥
parseClaimsJws(jwt).
getBody();
return claims;
}