用户在用户微服务中登录,如何又访问其他的微服务而不需要登录了?
这里需要在用户微服务里将用户信息加密生成token。在网关中配置能检验令牌.
并且,在网关中需要进行权限校验
我们可以采取JWT的方式来实现鉴权校验
一.什么是JWT
JSON Web Token(JWT)是一个非常轻巧的规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
JWT总结:
JWT是用于微服务之间传递用户信息的一段信息加密字符串,该字符串是一个JSON格式。
各个微服务可以根据该JSON字符串识别用户的身份信息。
二.JWT的构成
一个JWT实际上就是一个字符串,它由三部分组成,头部,载荷与签名
头部(Header)
头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。
{"type":"JWT","alg":"Hs256"}
在头部指明了签名算法是HS256算法。 我们进行BASE64编码http://base64.xpcha.com/,编码后的字符串如下:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
总结:
头部:
1.指定了令牌类型和令牌签名的算法
2.加密方式:Base64
载荷(playload)
载荷就是存放有效信息的地方。
载荷的属性也分三类:
预定义(Registered)
公有(public)
私有(private)
预定义的载荷 (标签中注册的声明(建议但不强制使用))
{
“sub”: “1”,
“iss”: “http://localhost:8000/auth/login”,
“iat”: 1451888119,
“exp”: 1454516119,
“nbf”: 1451888119,
“jti”: “37c107e4609ddbcc9c096ea5ee76c667”,
“aud”: “dev”
}
iss (issuer):签发人
sub (subject):主题
aud (audience):受众
exp (expiration time):过期时间
nbf (Not Before):生效时间,在此之前是无效的
iat (Issued At):签发时间
jti (JWT ID):编号
公有的载荷 (自己可以写一些别的属性放进去)
在使用 JWT 时可以额外定义的载荷。为了避免冲突,应该使用 IANA JSON Web Token Registry 中定义好的,或者给额外载荷加上类似命名空间的唯一标识。
私有载荷
私有载荷是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分在客户端可解密.
定义一个payload:
{"sub":"1234567890","name":"John Doe","admin":true}
然后将其进行base64加密,得到Jwt的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
载荷总结:
主要包括3部分:1.标准中注册的声明
2.公共的声明(不参与令牌校验)
3.私有声明(不参与令牌校验)
1+2+3->Base64加密
签证(signature[签名])
jwt的第三部分是一个签证信息(校验数据是否被篡改),这个签证信息由三部分组成:
header (base64后的)
payload (base64后的)
secret (秘钥->盐)
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.连接成一个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6I
kpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7Hg
Q
签名总结:Base64(头)+Base64(载荷)+秘钥(盐)->加密:采用头中指定的算法进行加密->密文(签名)
签名的作用:用于校验令牌是否被篡改
三.JWT的使用
1.依赖引入:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
- 创建测试类,代码如下
package com.example.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.Test;
import java.util.Date;
/**
* 令牌的生成与解析
*/
public class JwtTest {
/**
* 创建令牌
*/
@Test
public void testCreateToken(){
//构建JWT令牌的对象
JwtBuilder builder = Jwts.builder();
builder.setIssuer("Test"); //颁发者
builder.setIssuedAt(new Date()); //颁发时间
builder.setSubject("JWT 令牌测试"); //主题信息
builder.signWith(SignatureAlgorithm.HS256,"itcast"); //1.签名算法 2:秘钥(盐)
//输出令牌
String token = builder.compact();
System.out.println(token);
}
/**
* 令牌解析
*/
@Test
public void parseToken(){
String token = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJUZXN0IiwiaWF0IjoxNTc1ODY4NDA2LCJzdWIiOiJKV1Qg5Luk54mM5rWL6K-VIn0.PW48aDLfhLwrm9hy-8TcJj8ySudtjTU-odv4pqodvGA";
Claims claims = Jwts.parser()
.setSigningKey("itcast") //秘钥(盐)
.parseClaimsJws(token).getBody();//要解析的令牌对象
System.out.println(claims.toString()); //解析结果:{iss=Test, iat=1575868406, sub=JWT 令牌测试}
}
}
3.1设置过期时间
有时候我们并不希望签发的token是永久生效的,所有我们可以为token添加一个过期时间.
@Test
public void testCreateToken(){
//构建JWT令牌的对象
JwtBuilder builder = Jwts.builder();
builder.setIssuer("Test"); //颁发者
builder.setIssuedAt(new Date()); //颁发时间
builder.setExpiration(new Date(System.currentTimeMillis()+20000)); //过期时间20秒
builder.setSubject("JWT 令牌测试"); //主题信息
builder.signWith(SignatureAlgorithm.HS256,"itcast"); //1.签名算法 2:秘钥(盐)
//输出令牌
String token = builder.compact();
System.out.println(token);
}
.setExpiration(date)// 用于设置过期时间 ,参数为Date类型数据
3.2自定义载荷
@Test
/**
* 创建令牌
*/
@Test
public void testCreateToken(){
//构建JWT令牌的对象
JwtBuilder builder = Jwts.builder();
builder.setIssuer("Test"); //颁发者
builder.setIssuedAt(new Date()); //颁发时间
builder.setExpiration(new Date(System.currentTimeMillis()+7*24*3600*1000)); //过期时间
//自定义载荷信息
Map<String,Object> userInfo = new HashMap<>();
userInfo.put("company","101训练营");
userInfo.put("address","湖北");
userInfo.put("money",35000);
//将载荷加入
builder.addClaims(userInfo);
builder.setSubject("JWT 令牌测试"); //主题信息
builder.signWith(SignatureAlgorithm.HS256,"itcast"); //1.签名算法 2:秘钥(盐)
//输出令牌
String token = builder.compact();
System.out.println(token);
}
解析:
{iss=Test, iat=1575869490, exp=1576474290, address=湖北, money=35000, company=101训练营, sub=JWT 令牌测试}
4.创建工具类
JwtUtil
package com.changgou.entity;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
public class JwtUtil {
//有效期为
public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000 一个小时
public static final String JWT_KEY = "itcast";
/**
* 创建token * @param id * @param subject * @param ttlMillis * @return
*/
public static String createJWT(String id, String subject, Long ttlMillis) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if(ttlMillis==null){
ttlMillis=JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
SecretKey secretKey = generalKey();
JwtBuilder builder = Jwts.builder()
.setId(id) //唯一的ID
.setSubject(subject) // 主题 可以是JSON数据
.setIssuer("admin") // 签发者
.setIssuedAt(now) // 签发时间
.signWith(signatureAlgorithm, secretKey) //使用HS256对称加 密算法签名, 第二个参数为秘钥
.setExpiration(expDate);// 设置过期时间
return builder.compact();
}
private static SecretKey generalKey() {
byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
}