JWT介绍
JWT(JSON Web Token)是目前比较流行权限验证方案。其实它更像是一种规范。
平时基于session的验证方式
- 用户登录
- 服务器在当前会话(session)里面储存登录的信息比如用户名和id
- 服务器向用户返回一个session_id,写入用户的cookie(下一次用用户再次请求的时候的验证)
- 用户再次请求的时候带上cookie(session_id) ,服务器session_id查询之前储存的数据,得知用户身份.
但是session验证又2个问题
1.内存使用:session是使用的服务器的内存,如果人很多就需要很多内存
2.分布式:每个服务器都有session的话,服务器是分布式的,那么session的储存也需要一个分布式方案
为了解决这个问题,我们直接不保存session了就是一种解决方案(比如JWT)
JWT数据结构
写成一行
header.payload.signature
1.头部Header
header用来描述JWT的信息(使用base64解码得到信息)例如
{
"alg": "HS256",
"typ": "JWT"
}
alg:algorithm 就是算法,表示签名的算法,HS256是一种对称加密(签名部分会讲对称加密)
typ: type类型,表示这个token令牌是jwt类型.
2.payload负载
payload用来存放实际传输的数据
例如下面是官方的7个保留的字段
(使用base64解码得到信息)
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
当然你也可以自定义
{
username: "半亩方塘",
userRole: "admin"
}
3.(重要)signature签名
这个是防止数据篡改的关键,我们使用一个秘钥(secret)
,
前面的 header 和 payload 使用base64解码几乎都是明文的,只有signature才是签名加密的关键.
signature签名如何获得
前2部分header和payload分别base64加密,再通过秘钥secret,进行HS256这种对称式加密的出来,
对称式加密的secrete就像一个钥匙,加密和解密都需要它,没有它就没有办法加密和解密,
所以服务端的secrete十分重要,如果有了secrete用户就可以自己仿制签名
JWT代码实例
这里是maven项目
<!-- jwt依赖-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.1</version>
</dependency>
public class TokenUtil {
private static String secrete = "1234567890";
/**
* 传入用户角色进行验证
* @param userRole 用户角色比如admin
* @return 返回一个加密JWT token .
*/
public static String getToken(String userRole){
String token = JWT.create()//链式编程创建JWTCreator
.withClaim("userRole", userRole)//存放 payload 数据
.sign(Algorithm.HMAC256(secrete)); //使用secrete对称加密生成 signature
return token;
}
/**
* 解析token获得里面的payload数据
*/
public static Map<String,String> parseToken(String token){
HashMap<String, String> map = new HashMap<>();
//通过secret 和相同的对称加密算法反加密
DecodedJWT jwt = JWT.require(Algorithm.HMAC256(secrete))
.build().verify(token);
//获得你储存的payload信息
String userRole = jwt.getClaim("userRole").asString();
map.put("userRole",userRole);
return map;
}
}
test
String token = getToken("admin");
System.out.println("token="+token);
Map<String, String> map = parseToken(token);
String userRole = map.get("userRole");
System.out.println("解析token获得数据userRole="+userRole);
result
token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyUm9sZSI6ImFkbWluIn0.dWOUbJfnKAgTKw8OpBg1ooy-yqKrOjMU5aibs9CKv0o
解析token获得数据userRole=admin
拓展:这个私钥secrete是固定的,为了加强安全,你甚至可以使用动态的secrete私钥,例如
动态私钥 = 静态私钥 + 用户的ip,
这样即使别人得到了用户的token,也会因为ip不一致而访问失败.