Bootstrap

JAVA中应用JWT进行token会话

一、概述

        JWT是一种用于身份验证和授权的开放标准(RFC 7519),它定义了一种紧凑且自包含的方式来在不同系统之间传输信息。JWT由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。

  • 头部包含了描述该JWT的元数据,通常包含了使用的算法和令牌类型。
  • 负载包含了要传输的信息,可以自定义一些声明,例如用户ID、角色等。
  • 签名是对头部和负载的签名,用于验证JWT的真实性和完整性。
  • JWT是无状态的,可以在不同的请求之间传递,服务器不需要存储任何信息。由于JWT是自包含的,可以直接从JWT中读取信息,无需通过数据库或其他方式进行查询。
  • JWT可以被用于各种场景,例如身份验证、用户授权、信息交换等。
基于session认证所显露的问题

Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。

扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。

CSRF (跨站请求伪造):因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

为了解决这些问题,出现了JWT和Token。

二、JWT的三部分

 JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

1.头部

jwt的头部承载两部分信息:

  • 声明类型,这里是jwt

  • 声明加密的算法 通常直接使用 HMAC HS256

完整的头部就像下面这样的JSON:

 {'typ': 'JWT','alg': 'HS256'}-->重新进行编码(不是加密)

然后将头部进行base64转码,构成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

2.载荷

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

  • 标准中注册的声明

  • 公共的声明

  •  公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务   需要的必要信息.但不建议添加敏感信息(例如密码),因为该部分在客户端可解密. id,用户名,头像名

  • 私有的声明

定义一个payload

{ "sub": "1234567890", "name": "John Doe","admin": true}

然后将其进行base64转码,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

3.签名(类似于比特币)

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)

  • payload (base64后的)

  • secret

这个部分需要base64转码后的header和base64转码后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

 TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

        JWT生成Token的过程类似于比特币生成新区块的过程,这里的签名相当于是私钥,只有自己知道且难以用方法来破解。

三、Java中使用JWT生成token

1.引入JWT依赖

<dependency>

      <groupId>com.auth0</groupId>

      <artifactId>java-jwt</artifactId>

      <version>3.8.2</version>

</dependency>

注:可以根据个人情况引入,没必要必须用这个。

 2.生成token

/**
     * 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<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;
    }

3.验证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;
    }
}

4.获取token中的负载信息

public class main {

    public static void main(String[] args) {
        String token;
        Date expireDate = new Date(new Date().getTime() + 10*1000);
        //秘钥及加密算法
        Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
        //设置头部信息
        Map<String,Object> header = new HashMap<String, Object>();
        header.put("typ","JWT");
        header.put("alg","HS256");
        //携带id,账号信息,生成签名
        token = JWT.create()
                .withHeader(header)
                .withClaim("id",123)
                .withClaim("account",123)
                .withExpiresAt(expireDate)
                .sign(algorithm);
        DecodedJWT verify = JWT.require(algorithm).build().verify(token);
        System.out.println(verify.getClaim("id").asInt());
        System.out.println(verify.getClaim("account").asInt());
    }
}

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;