Json Web Token

一、认识JWT(Json Web Token)

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,一般来说如果是使用session或者cookie来实现,但是这两者都有一定的局限性。而我们使用JWT去实现跨域认证的话,就可以将JWT放在请求头里面(也有人会把它放在post请求的数据体里,但是这种方法比较少见)。

  1.JWT的数据结构

JWT解析出来之后其实就是一个Json,但是在传送的过程中,它是一串很长的字符串,中间用“.”分割成三个部分

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

写成一行就是

Header.Payload.Signature

举一个我在真正开发时遇到的一个JWT,里面的id_token就是一个JWT,可以看到它就是用两个“.”去分割开的。

 

 

 

  2.Header(头部)

Header 部分是一个 JSON 对象,描述 JWT 的元数据。

{
  "alg": "HS256",
  "typ": "JWT"
}

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT

最后,将上面的 JSON 对象使用 Base64URL 算法成字符串。说到这个,如果你的签名算法是别的算法的话,去解析JWT的时候会报错

Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance

这时候你要去检查一下你的密钥是否正确,我当时的情况就是公钥搞错了,不过我看网上也有人说什么可以设置一下算法的类型,但是看不懂是怎么操作的,全网就搜到了那个方法,全都是一模一样,复制来复制去的,很没意思。

  3.Payload(负载)

Payload是JWT最重要的数据,里面会携带很多信息,我们要实现登录认证一般都在里面拿取到我们想要的数据。JWT 规定了7个官方字段供选用。

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

除了官方字段,我们还可以在生成JWT的时候定义部分私有字段名,下面是我工作是遇到的JWT,他们就定义了自己的私有字段,将payload解析之后就是下面的图片

  4.Signature(签名)

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

  5.JWT的使用

一般是放在请求头里面,注意“Bearer”和“token”之间有一个空格。

Authorization: Bearer <token>

二、解析JWT

解析JWT网上有很多个jar可以拿来使用,我这里使用的jose4j来解析。

    /**
     * 验证Jwt并拿到email值
     *
     * @param jwtTicket Jwt票据
     * @param publicKeyStr 公钥
     */
    public String validateAndGetToken(String jwtTicket, String publicKeyStr) throws Exception {

        JsonWebSignature jws = new JsonWebSignature();
        jws.setCompactSerialization(jwtTicket);
        JsonWebKey jsonWebKey = JsonWebKey.Factory.newJwk(publicKeyStr);
        String keyId = jsonWebKey.getKeyId();
        jws.setKey(jsonWebKey.getKey());

        boolean verifySignature = jws.verifySignature();
        if (!verifySignature) {
            throw new Exception("Invalid signature!");
        }

        String payload = jws.getPayload();
        JwtClaims claims = JwtClaims.parse(payload);
        NumericDate expirationTime = claims.getExpirationTime();
        Map<String, Object> claimsMap = claims.getClaimsMap();
        if (expirationTime == null) {
            logWarn("Invalid signature!");
            return null;
        }
        if (!expirationTime.isAfter(NumericDate.now())) {
            logWarn("Jwt ticket expired!");
            return null;
        }
        String username = (String) claimsMap.get("email");
        return username;
    }

这里claimsMap.get("email")就是拿到邮箱地址,因为我做的是邮箱的单点登录,所以这里是email,根据具体情况去获取具体的字段就可以了。

 参考链接

http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

posted @ 2022-02-10 15:46  拿着放大镜看世界  阅读(1047)  评论(0编辑  收藏  举报