目录
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT 可以使用 HMAC 算法或者是 RSA 的公私秘钥对进行签名。
我们知道,http 协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据 http 协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为 cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于 session 认证。
但是这种基于 session 的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来.
Session:每个用户经过认证后,我们都要在服务端做一次记录,以方便用户下一次的请求的鉴别,通常session保存在内存中,这样一来,随着用户的数量的增多,我们的服务端的开销明显会增大
扩展性:用户认证之后,服务端做认证记录,认证被保存在内存中的话,这意味着用户下次请求还是要请求服务端,才能拿到授权的资源,这样限制了负载均衡器的能力,也就意味着限制了应用的扩展
CSRF(跨站请求伪造):因为基于cookie来进行用户识别的,cookie如果被截获,那么用户就很容易的收到跨站请求的伪造攻击
它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token 认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
流程:
用户使用账号和密码发出 post 请求;
服务器使用私钥创建一个 jwt;
服务器返回这个 jwt 给浏览器;
浏览器将该 jwt 串在请求头中像服务器发送请求;
服务器验证该 jwt;
返回响应的资源给浏览器
身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录中比较广泛的使用了该技术。信息交换在通信的双方之间使用JWT 对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。
1.简洁(Compact): 可以通过 URL,POST 参数或者在 HTTP header 发送,因为数据量小,传输速度也很快
2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
3.因为 Token 是以 JSON 加密的形式保存在客户端的,所以JWT 是跨语言的,原则上任何 web 形式都支持。
4.不需要在服务端保存会话信息,特别适用于分布式微服务。
5.由于私钥加密的缘故,也自然而然会更加的安全
- <dependency>
- <groupId>com.auth0</groupId>
- <artifactId>java-jwt</artifactId>
- <version>3.8.2</version>
- </dependency>
- /**
- * 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("ZCEQIUBFKSJBFJH2020BQWE");
- //设置头部信息
- Map
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("ZCEQIUBFKSJBFJH2020BQWE");
- 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("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);
- }
用户登录成功后将用户 id 和账号存储到 token 中返回给客户端,之后客户端每次请求将 token 发送到服务器端验证, 在服务器中进行验证.