JWT官网: https://jwt.io/
JWT(Java版)的github地址:https://github.com/jwtk/jjwt
一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。
URL
,POST
参数或者在HTTP header
发送,因为数据量小,传输速度也很快Token
是以JSON
加密的形式保存在客户端的,所以JWT
是跨语言的,原则上任何web形式都支持。JWT标准的token包含三部分:
签名解决了数据传输过程中参数被篡改的风险
一般而言,加密算法对于不同的输入产生的输出总是不一样的,如果有人对Header以及Payload的内容解码之后进行修改,再进行编码的话,那么新的头部和载荷的签名和之前的签名就将是不一样的。而且,如果不知道服务器加密的时候用的密钥的话,得出来的签名也一定会是不一样的。
服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次签名。如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被别人动过的,我们应该拒绝这个Token
io.jsonwebtoken
jjwt
0.9.1
package com.application.utils;
import java.util.Calendar;
import java.util.Date;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class TokenUtil {
/**
* 加密
* @author Jason
* @date 2019-03-29
* @param id 用户id
* @param username 用户名称
* @param password 用户密码
* @param deviceId 设备号
* @param formatDate 过期日期
* @return 加密的token
*/
public static String createToken(String id, String username, String password, String deviceId, String formatDate) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = Calendar.getInstance().getTimeInMillis();
Date now = new Date(nowMillis);
// 加密秘钥
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("123456789");
SecretKeySpec signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
JwtBuilder builder = Jwts.builder()
.setIssuedAt(now)
.setId(id)
.setIssuer(username)
.setSubject(password)
.setAudience(deviceId)
.signWith(signatureAlgorithm,signingKey);
// 设置过期日期
Date expirationDate = null;
if (null != formatDate) {
try {
expirationDate = DateUtil.stringToDate(formatDate);
} catch (Exception e) {
expirationDate = getDefultExpirationDate();
}
if(null == expirationDate || expirationDate.before(new Date())){
expirationDate = getDefultExpirationDate();
}
} else {
expirationDate = getDefultExpirationDate();
}
builder.setExpiration(expirationDate);
return builder.compact();
}
/**
* 获取默认的过期时间,默认是七天以后
* @author Arwen Liu
* @date 2018-10-29
* @return Date
*/
private static Date getDefultExpirationDate(){
long timeInMillis = Calendar.getInstance().getTimeInMillis();
Long time = 1 * 1000 * 60 * 60 * 24 * 7l;
time += timeInMillis;
return new Date(time);
}
/**
* 解密
* @author Arwen Liu
* @date 2018-10-29
* @param token
* @return Claims
*/
public static Claims parseToken(String token) {
Claims claims = Jwts.parser()
// s解密密钥,需要和 加密秘钥一致
.setSigningKey(DatatypeConverter.parseBase64Binary("123456789"))
.parseClaimsJws(token).getBody();
return claims;
}
public static void main(String[] args) {
String secretToken = createToken("1001", "staff", "123456","deviceId", "2018-12-28");
System.out.println("加密后---->" + secretToken);
// ss解密
Claims claims = parseToken(secretToken);
System.out.println("解密后---->");
System.out.println("id: " + claims.getId());
System.out.println("username: " + claims.getIssuer());
System.out.println("password: " + claims.getSubject());
System.out.println("deviceId: "+ claims.getAudience());
System.out.println("expiration: " + DateUtil.dateToString(claims.getExpiration(), "yyyy-MM-dd"));
}
}
在退出登录时怎样实现JWT Token失效呢?
退出登录, 只要客户端端把Token丢弃就可以了,服务器端不需要废弃Token。
怎样保持客户端长时间保持登录状态?
服务器端提供刷新Token的接口, 客户端负责按一定的逻辑刷新服务器Token。
服务器端是否应该从JWT中取出userid用于业务查询?
REST API是无状态的,意味着服务器端每次请求都是独立的,即不依赖以前请求的结果,因此也不应该依赖JWT token做业务查询, 应该在请求报文中单独加个userid 字段。
为了做用户水平越权的检查,可以在业务层判断传入的userid和从JWT token中解析出的userid是否一致, 有些业务可能会允许查不同用户的数据。
如何防范Replay Attacks
重复攻击
所谓重复攻击就是攻击者发送一个后端服务器已接收过的包,来达到攻击系统的目的。
比如在浏览器端通过用户名/密码验证获得签名的Token被木马窃取。即使用户登出了系统,黑客还是可以利用窃取的Token模拟正常请求,而服务器端对此完全不知道,因为JWT机制是无状态的。
可以在Payload里增加时间戳并且前后端都参与来解决:
1.前端生成token时,在payload里增加当前时间戳
2.后端接收后,对解析出来的时间戳和当前时间进行判断,
3.如果相差特定时间内(比如2秒),允许请求否则判定为重复攻击
参考 https://www.jianshu.com/p/e88d3f8151db
https://www.jianshu.com/p/836df92c06eb
https://blog.csdn.net/why15732625998/article/details/78534711
https://www.jianshu.com/p/e88d3f8151db
http://www.cnblogs.com/xiekeli/p/5607107.html