JWTtoken登录校验
- session用户认证的一般流程
- JWTToken认证的流程
- JWT token的组成
- 头部(header)
- payload 负载
- signature签名
- 三个部分组合形成token
- java中使用JWTToken
JWT (Json Web Token) 是为了网络应用环境间传递声明而执行的一种基于JSON的开放标准。
JWT可以校验用户的身份,传递用户的身份信息,一般用在用户登录上。
session用户认证的一般流程
- 用户输入账号密码, 向服务器发送请求
- 服务器验证账号密码是否正确, 如果正确则在该用户的session回话里保存相关的信息如id, 登录时间等
- 服务器向用户返回一个Cookie, 存着sessionId
- 用户再次请求服务器时带上sessionId.服务器根据sessionId从服务器保存的session中获取的信息.
这种方式在单服务器场景下没有太大问题
但在服务器集群下扩展性较差, session存在于不同的服务器, 则用户下次请求又必须分配到上次请求的服务器, 影响了负载均衡的能力.
CSRF: 跨站请求伪造, 如果截获了用户Cookie中的sessionId, 则很容易会受到跨站请求伪造的攻击
使用JWTToken则不会遇到该问题, 因为使用JWTToken服务器就不保存信息了, 而是token里保存着编码的信息, 并对附带一个加密的签名, 服务器接收到token可直接解密进行认证.
JWTToken认证的流程
- 用户输入账号密码, 向服务器发送请求.
- 服务器验证账号密码,返回一个加密的保存有用户信息的token
- 用户再次请求时带上token
- 服务器解析token, 取出token中的信息进行认证
JWT token的组成
三个部分
- Header 头部
- Payload 负载
- Signature 签名
头部(header)
{
"alg": "HS256",
"typ": "JWT"
}
alg : 加密类型
typ : JWT token的类型
alg | 算法 | 介绍 |
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 |
payload 负载
负载主要是存放有效信息的地方
标准中注册的声明
建议但不强制使用
iss : jwt签发者
sub : jwt所面向的用户
aud : 接收jwt的一方
exp : jwt的过期时间, 要大于签发时间
nbf : 定义一个时间前, token都是不可用的
iat : jwt的签发时间
jti : jwt的唯一身份标识, 作为一次性token, 避免重放攻击
自定义数据
可以加入一些自己定义的数据, 用来进行用户认证
不建议存放敏感信息, 因为这部分内容不会进行加密, 而是采用base64进行编码.
id : 22
name : zhangs
signature签名
签名为jwt的第三个部分
jwt的签名是根据headers头部和payload负载通过头部中声明的加密方式进行加盐secret组合加密
secret存在服务器中, 服务器通过这个签名验证token的合法性, 防止伪造的token.
三个部分组合形成token
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload)+ "." +
your-256-bit-secret
)
样例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MjIsImV4cCI6MTU1NzU4Mjc1NSwiaWF0IjoxNTU2OTc3OTU1fQ.icWwR1ysVb4p30XywCck6Fkzn6Oep45zG50NmRLc3BE
解析出来的headers
{
"alg": "HS256",
"typ": "JWT"
}
解析出来的payload
{
"id": 22,
"exp": 1557582755,
"iat": 1556977955
}
java中使用JWTToken
导入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.5.0</version>
</dependency>
创建token
Map<String, Object> map = new HashMap<String, Object>();
map.put("alg", "HS256");
map.put("typ", "JWT");
String token = JWT.create()
.withHeader(map) // 添加头部
.withClaim("id", id) // 添加自定义数据
.withExpiresAt(experiesDate) // 设置过期的日期
.withIssuedAt(iatDate) // 签发时间
.sign(Algorithm.HMAC256(SECRET)); // 加密
校验token
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
DecodedJWT jwt = null;
try {
jwt = verifier.verify(token);
} catch (Exception e) {
throw new Exception("登录过期");
}
return jwt.getClaims();
在做登录的intercepter中加入验证逻辑
String token = request.getHeader("token"); // 获取请求头里的token
if (token == null) {
// 跳转返回未登录
request.getRequestDispatcher("/user/need_login.do").forward(request, response);
logger.info("未登录");
} else {
try {
Map<String, Claim> map = JWTUtil.verifyToken(token); // 该方法验证失败会抛出异常
int id = map.get("id").asInt(); // 没有id也会抛出异常
request.setAttribute("id", id); // 传递参数id
return true; // 验证成功放行
} catch (Exception e) { // 抛出异常进行跳转
request.getRequestDispatcher("/user/need_login.do").forward(request, response);
logger.info("登录过期");
}
}