请问你是学Java的吗?那Jwt你知道吗?

「这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

Jwt是什么?

概述:

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

Jwt能干什么?

场景: 可用于跨域身份验证

传统身份验证过程如下:

  1. 用户向服务器发送用户名和密码。
  2. 验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中。
  3. 服务器向用户返回session_id,session信息都会写入到用户的Cookie。
  4. 用户的每个后续请求都将通过在Cookie中取出session_id传给服务器。
  5. 服务器收到session_id并对比之前保存的数据,确认用户的身份。

缺点: 此方案会导致逐渐增大服务器的负荷,并且cookie存储在客户端中容易被高手截获进而进行伪造,导致信息安全的问题发生。

相比较传统的session广播存入cookie中的解决方案,Jwt更加的灵活通过客户端保存数据,而服务器不用保存会话的数据,且自身的设计可以有效的防止信息被非法伪造。

Jwt怎么使用?

1、首先看看Jwt的组成

典型的,一个JWT看起来如下图:

image.png

该对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串。

每一个子串表示了一个功能块,总共有以下三个部分:JWT头、有效载荷和签名

JWT头:JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。

    {
      "alg": "HS256",
      "typ": "JWT"
    }
复制代码

在上面的代码中,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。

有效载荷:有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。(都为可选设置)

    iss:发行人
    exp:到期时间
    sub:主题
    aud:用户
    nbf:在此之前不可用
    iat:发布时间
    jti:JWT ID用于标识该JWT
复制代码

除以上默认字段外,我们还可以自定义私有字段,如下例:

    {
      "sub": "123456",
      "name": "mingru",
      "acl": true
    }
复制代码

请注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。

JSON对象也使用Base64 URL算法转换为字符串保存。

签名哈希

  1. 签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。

  2. 首先,需要指定一个secret(密钥)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用Jwt头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。

        HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret)
    复制代码
  3. 在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。

Base64URL算法

  1. 如前所述,JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似,稍有差别。
  2. 作为令牌的JWT可以放在URL中(例如index/?token=xxx)。 Base64中用的三个字符是"+","/"和"=",由于在URL中有特殊含义,因此Base64URL中对他们做了替换:"="去掉,"+"用"-"替换,"/"用"_"替换,这就是Base64URL算法。

2、Jwt的大体工作流程

  1. 用户在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,之后,当用户与服务器通信时,客户在请求中发回JSON对象。
  2. 服务器仅依赖于这个JSON对象来标识用户。为了防止用户篡改数据,服务器将在生成对象时添加签名。而当用户再次访问同一个系统中的其他服务时请求头中便会携带一个token字符串,用户身份的验证通过解析token和之前合法的相对比,查看是否有被修改过。

服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。

3、如何防止用户信息(有效载荷)被高手获取到并且被修改呢?

通过Jwt的token的生成就可以解释,再生成签名哈希时HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret),在此步骤生成中,每一个数据体生成的签名哈希是独一无二的,而当你确实获取到用户的信息时,虽然可以至少其中的数据信息,但是如果想要对其进行非法的修改时,可想而知,一次对其进行验证时生成的token值肯定会产生改变,这样服务器就会对此次访问做出判断,阻止其继续操作。

Jwt的token字符串生成代码

//Jwt依赖
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
</dependency>
复制代码
    public class JwtUtils {
    
        //设置的密钥
        private static final String SIGN="@das$25*j^";
    
        //获取token字符串
        public static String getToken(Map<String,String> map){
            //设置token过期时间
            Calendar instance = Calendar.getInstance();
            instance.add(Calendar.DATE,7);
    
            JWTCreator.Builder builder = JWT.create();
            map.forEach((k,v)->{
                builder.withClaim(k,v);
            });
            String token = builder.withExpiresAt(instance.getTime())  //指定过期时间
                    .sign(Algorithm.HMAC256(SIGN));     //指定加密算法
    
            return token;
        }
    }
复制代码

验证合法代码

    //验证是否合法
    public static DecodedJWT verify(String token){
        return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
    }
复制代码

至此结束!希望本文能够对你有所帮助。。。