开发程序需要设计用户鉴权,这次跟桌面客户端交互,采用token进行鉴权,使用JWT进行鉴权(虽然有人说不要用JWT了,不过还是试试了解一下。)
开发时参考了其他博主的内容学习了解,下面JWT的相关知识内容就直接复制过来,出处:JWT全面解读、使用步骤_陈袁的博客-CSDN博客
JWT基本使用
在pom.xml引入java-jwt
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.1</version>
</dependency>
写一个JWT工具类,包含根据信息生成token字符串,解密验证token,读取token中的userId。因为过期时间参数和签名方法都是静态的,所以配置文件设置的时间参数用set方法注入才能生效。
@Component
@Slf4j
public class JwtUtil {
private static long EXPIRE_TIME;
// private static final long EXPIRE_TIME = 30 * 60 *1000;
private static final String TOKEN_SECRET = "da0362fda20d424cac8843617fd62166";
public static String sign(String userName,Integer userId){
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
Map<String,Object> header = new HashMap<>(2);
header.put("typ","JWT");
header.put("alg","HS256");
return JWT.create().withHeader(header).withClaim("userNmae",userName)
.withClaim("userId",userId).withExpiresAt(date).sign(algorithm);
}
/**
* 解密Token
*
* @param token
* @return
* @throws Exception
*/
public static Map<String, Claim> verifyToken(String token) {
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build();
jwt = verifier.verify(token);
} catch (Exception e) {
e.printStackTrace();
log.error("解密token报错",e);
// token 校验失败, 抛出Token验证非法异常
}
return jwt.getClaims();
}
public static Integer getUserId(String token){
DecodedJWT jwt = JWT.decode(token);
Claim userIdClaim = jwt.getClaim("userId");
return userIdClaim.asInt();
}
@Value("${jwt.expireTime}")
public void setExpireTime(long expireTime){
JwtUtil.EXPIRE_TIME = expireTime;
}
}
JWT消息构成
一个token分3部分,按顺序为
- 头部(header)
- 其为载荷(payload)
- 签证(signature)
由三部分生成token
3部分之间用“.”号做分隔。例如eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
验证koten地址
头部
Jwt的头部承载两部分信息:
声明类型,这里是jwt
声明加密的算法 通常直接使用 HMAC SHA256
JWT里验证和签名使用的算法,可选择下面的。
JWS | 算法名称 | 描述 |
HS256 | HMAC256 | HMAC with SHA-256 |
HS384 | HMAC384 | HMAC with SHA-384 |
HS512 | HMAC512 | HMAC with SHA-512 |
RS256 | RSA256 | RSASSA-PKCS1-v1_5 with SHA-256 |
RS384 | RSA384 | RSASSA-PKCS1-v1_5 with SHA-384 |
RS512 | RSA512 | RSASSA-PKCS1-v1_5 with SHA-512 |
ES256 | ECDSA256 | ECDSA with curve P-256 and SHA-256 |
ES384 | ECDSA384 | ECDSA with curve P-384 and SHA-384 |
ES512 | ECDSA512 | ECDSA with curve P-521 and SHA-512 |
使用代码如下
// header Map
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
playload
载荷就是存放有效信息的地方。基本上填2种类型数据
-标准中注册的声明的数据
-自定义数据
由这2部分内部做base64加密。最张数据进入JWT的chaims里存放。
标准中注册的声明 (建议但不强制使用)
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
使用方法
JWT.create().withHeader(map) // header
.withClaim("iss", "Service") // payload
.withClaim("aud", "APP")
.withIssuedAt(iatDate) // sign time
.withExpiresAt(expiresDate) // expire time
自定义数据
这个就比较简单,存放我们想放在token中存放的key-value值
使用方法
JWT.create().withHeader(map) // header
.withClaim("name", "cy") // payload
.withClaim("user_id", "11222");
签名signature
jwt的第三部分是一个签证信息,这个签证信息算法如下:
base64UrlEncode(header) + "." + base64UrlEncode(payload)+your-256-bit-secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
基本上至此,JWT的API相关知识已经学完了,但是API不够有好,不停的用withClaim放数据。不够友好。下面推荐一款框架,相当于对JWT的实现框架
使用JwtUtil的sign方法生成token字符串,我选择的是将token放在response的header中,token的签名信息包括userId和用户名,加密秘钥TOKEN_SECRET使用UUID生成。token生成后,向客户端返回用户信息之前在redis里也存储了token。
客户端获取后也将token放在request的header中发送请求,拦截器鉴权时从request中获取携带的token进行解密校验。同时获取userId并对redis存储的token进行鉴权。
因为懒没有设计活跃用户刷新token时间,采用的固定到期时间鉴权。