JWT(Json web token)认证详解

JWT的定义:

  JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。

JWT特点:
  简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
  自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库

JWT结构:

JWT主要包含Header 头部  Payload 负载  Signature 签名,顺序是 header.payload.signature 三部分之间用英语句号’.'隔开  
 

Header头部

头部包含了两部分,token 类型(“JWT”)和采用的加密算法(HMAC SHA256或者RSA等等)

例如:

然后,用Base64对这个JSON编码就得到JWT的第一部分

Payload负载

这部分就是我们存放信息的地方了,你可以把用户 ID 等信息放在这里,JWT 规范里面对这部分有进行了比较详细的介绍,常用的由 iss(签发者),exp(过期时间),sub(面向的用户),aud(接收方),iat(签发时间)。

同样的,它会使用 Base64 编码组成 JWT 结构的第二部分。

Signature签名

  前面两部分都是使用 Base64 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用Base64编码后的 header 和 payload 以及我们提供的一个密钥,然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过。三个部分通过.连接在一起就是我们的 JWT 了,它可能长这个样子,长度貌似和你的加密算法和私钥有关系。

eyJhbGciOiJIUzI1NiIsIlR5cGUiOiJKd3QiLCJ0eXAiOiJKV1QifQ.eyJsb2dpblRpbWUiOiIyMDIxLTEwLTI3VDE1OjIxOjU4LjU3OCIsInVzZXJOYW1lIjoi5rGf5Y2XIiwiZXhwIjoxNjM1MzIxMTE4fQ.lUojZpIGx0a65s5FrgYlQio0vf3jOuqWOTP-DUjzGh0
注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了

JSON Web Tokens是如何工作的?

一般是在请求头里加入Authorization,并加上Bearer标注:

fetch('api/user/1', {
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:

优点

  • 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展

安全相关

  • 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
  • 保护好secret私钥,该私钥非常重要。
  • 如果可以,请使用https协议

最后附上Token工具类

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class TokenUtil {

    /**
     * token过期时间
     */
    private static final long EXPIRE_TIME = 30 * 60 * 1000;
    /**
     * token秘钥
     */
    private static final String TOKEN_SECRET = "secret";


    /**
     * 生成token,30分钟过期
     *
     * @param userName  用户名
     * @param loginTime 登录时间
     * @return 生成的token
     */
    public static String sign(String userName, LocalDateTime loginTime) {
        try {
            // 设置过期时间
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            System.out.println(date);
            // 私钥和加密算法
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            // 设置头部信息
            Map<String, Object> header = new HashMap<>(3);
            header.put("Type", "Jwt");
            header.put("alg", "HS256");
            // 返回token字符串
            return JWT.create()
                    .withHeader(header)
                    // 设置token中需要加载的用户信息 存储自己想要留给前端的内容
                    .withClaim("userName", userName)
                    .withClaim("loginTime", loginTime.toString())
                    .withExpiresAt(date)
                    .sign(algorithm);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 检验token是否正确
     */
    public static boolean verify(String token) {
        try {
            //设置签名的加密算法:HMAC256
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            verifier.verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取token中信息 userName
     */

    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("userName").asString();

        } catch (JWTDecodeException e) {
            e.printStackTrace();
        }
        return null;
    }
}
我话讲完!谁赞成?谁反对?
原文地址:https://www.cnblogs.com/wffzk/p/15471800.html