Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一
种基于 JSON 的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于
通信双方之间以 JSON 对象的形式安全的传递信息。因为数字签名的存在,这些
信息是可信的,JWT 可以使用 HMAC 算法或者是 RSA 的公私秘钥对进行签名
为了让我们的应用能识别是哪个用户发出的请求,我们只能在服
务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其
保存为 cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请
求来自哪个用户了,这就是传统的基于 session 认证。
但是这种基于 session 的认证使应用本身很难得到扩展,随着不同客户端用
户的增加,独立的服务器已无法承载更多的用户,而这时候基于 session 认证应
用的问题就会暴露出来.
Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做
一次记录,以方便用户下次请求的鉴别,通常而言 session 都是保存在内存中,
而随着认证用户的增多,服务端的开销会明显增大。
扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存
中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授
权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味
着限制了应用的扩展能力。
CSRF (跨站请求伪造):因为是基于 cookie 来进行用户识别的, cookie 如果
被截获,用户就会很容易受到跨站请求伪造的攻击。
基于 token 的鉴权机制类似于 http 协议也是无状态的,它不需要在服务端
去保留用户的认证信息或者会话信息。这就意味着基于 token 认证机制的应用
不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利
身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含
JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由
于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录
中比较广泛的使用了该技术。信息交换在通信的双方之间使用 JWT 对数据进行
编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送
的信息是没有经过伪造的。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiw
ibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab3
0RMHrHDcEfxjoYZgeFONFh7HgQ
header
jwt 的头部承载两部分信息:
声明类型,这里是 jwt
声明加密的算法 通常直接使用 HMAC HS256
完整的头部就像下面这样的 JSON:
{
'typ': 'JWT',
'alg': 'HS256' }
然后将头部进行 base64 转码,构成了第一部分.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
playload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效
信息包含三个部分
标准中注册的声明
公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其
他业务 需要的必要信息.但不建议添加敏感信息(例如密码),因
为该部分在客户端可解密. id,用户名,头像名
私有的声明
定义一个 payload
{ "sub": "1234567890",
"name": "John Doe", "admin": true
}
然后将其进行 base64 转码,得到 Jwt 的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4i
OnRydWV9
signature
jwt 的第三部分是一个签证信息,这个签证信息由三部分组成:
header (base64 后的)
payload (base64 后的)
fsecret
这个部分需要 base64 转码后的 header 和 base64 转码后的 payload 使用.连
接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,
然后就构成了 jwt 的第三部分。
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
com.auth0
java-jwt
3.8.2
/**
* jwt 生成 token
* @param id
* @param account * @return
*/
public static String token (Integer id, String account){
String token = "";
try {
//过期时间 为 1970.1.1 0:0:0 至 过期时间 当前的毫秒值 + 有效时间
Date expireDate = new Date(new Date().getTime() + 10*1000);
//秘钥及加密算法
Algorithm algorithm = Algorithm.HMAC256("随便一串安全密钥");
//设置头部信息
Map<String,Object> header = new HashMap<>();
header.put("typ","JWT");
header.put("alg","HS256");
//携带 id,账号信息,生成签名
token = JWT.create()
.withHeader(header)
.withClaim("id",id)
.withClaim("account",account)
.withExpiresAt(expireDate)
.sign(algorithm);
}catch (Exception e){
e.printStackTrace();
return null;
}return token;
}
public static boolean verify(String token){
try {
//验签
Algorithm algorithm = Algorithm.HMAC256("随便一串安全密钥");
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token); return true;
} catch (Exception e) {//当传过来的 token 如果有问题,抛出异常
return false;
}
}
/**
* 获得 token 中 playload 部分数据,按需使用
* @param token
* @return
*/
public static DecodedJWT getTokenInfo(String token){
return JWT.require(Algorithm.HMAC256("随便一串安全密钥")).build().verify(token);
}