JWT学习运用

本文深入讲解了JWT(JSON Web Token)认证机制的工作原理及其在实际应用中的实现过程,对比了JWT认证与传统认证方式的不同之处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系统认证方式

session认证

​ http协议由于本身是无状态的协议,那就意味着当有用户向系统使用账户名称和密码进行用户认证之后,下一次请求还要再一次用户认证才行。因为我们不能通过http协议知道是哪个用户发出的请求,所以如果要知道是哪个用户发出的请求,那就需要在服务器保存一份用户信息(保存至session),然后在认证成功后返回cookie值传递给浏览器,那么用户在下一次请求时就可以带上cookie值,服务器就可以识别是哪个用户发送的请求,是否已认证,是否登录过期等等。这就是传统的session认证方式。

​ session认证的缺点其实很明显,由于session是保存在服务器里,所以如果分布式部署应用的话,会出现session不能共享的问题,很难扩展。于是乎为了解决session共享的问题,又引入了redis,即token认证。

token认证

​ 这种方式跟session的方式流程差不多,不同的地方在于保存的是一个token值到redis,token一般是一串随机的字符(比如UUID),value一般是用户ID,并且设置一个过期时间。每次请求服务的时候带上token在请求头,后端接收到token则根据token查一下redis是否存在,如果存在则表示用户已认证,如果token不存在则跳到登录界面让用户重新登录,登录成功后返回一个token值给客户端

​ 优点是多台服务器都是使用redis来存取token,不存在不共享的问题,所以容易扩展。缺点是每次请求都需要查一下redis,会造成redis的压力,还有增加了请求的耗时,每个已登录的用户都要保存一个token在redis,也会消耗redis的存储空间。

JWT认证

  • JWT(JSON Web Token),通过数字签名的方式,以JSON对象为载体,在不同的服务终端之间安全传输信息,该信息可以被验证和信任,因为它是数字签名的。JWT本质其实就是字符串而已。
  • JWT最常见的场景就是授权认证,一旦用户登录成功,后续的每个请求将包含JWT,系统在每次处理用户请求的之前,都会先进行JWT安全校验,通过之后再进行相应处理。
  • JWT就是一种认证机制,让后台知道该请求是来自于受信的客户端。

JWT基本流程:

  1. 用户进行登录,登录请求发送给Authentication Server
  2. Authentication Server进行用户校验,成功后创建JWT字符串返回给客户端
  3. 客户端请求接口时,在请求带上JWT信息
  4. Application Server验证JWT合法性,如果合法则继续调用应用接口返回结果。

JWT认证与Session认证

JWT不需要存储,而是在服务端根据前端传回的token进行解密比对处理
session认证是在用户登录之后,给浏览器返回一个sessionid,另外在服务器端同时存储sessionid及必要的用户信息,在用户使用cookie把sessionid传回服务端,服务端基于sessionid去数据库查询,若查到则表示用户还在活跃期,同时也可以获取到存储的必要用户信息。
相同点就是都需要使用cookie。

JWT认证与Token认证

​ Token需要查库验证token 是否有效,而JWT不用查库,直接在服务端进行校验,因为用户的信息及加密信息,和过期时间,都在JWT里,只要在服务端进行校验就行,并且校验也是JWT自己实现的。JWT认证关键在于生成JWT,和解析JWT这两个地方。

JWT的结构

JWT由3部分组成,用 " . "拼接的字符串,如下所示

eyhbGcioiJIUzI1NiIsInR5cCI6IkpXvC9.ey31c2vybmFtzSI617RvbSIsIn3vbGuioihzG1pbiIsInN1YiI6ImFkbw1uLXR1c3QiLCleHAiojE2MjMyMjM2NzUsImp0asT6ImQ2MTJjzjcxLwI5ZmUtNGMwNy04MzQwLTViowvizmMyNjExNy39.Fo59Y7rYNdc2AoidnSPrgg2XTYePuOyGz598h2gtabE

这三部分分别为:

  1. Header

    ​ JWT头,包含两个信息,一个为Type(token类型),一个为算法的名称alg(加密方式),我们会将这个信息进行Base64编码,就构成上述字符串的第一部分。

    {
    	'type':'JWT',
    	'alg':'HS256'
    }
    

    JWT里验证和签名使用的算法列表如下:

    JWS算法名称
    HS256HMAC256
    HS384HMAC384
    HS512HMAC512
    RS256RSA256
    RS384RSA384
    RS512RSA512
    ES256ECDSA256
    ES384ECDSA384
    ES512ECDSA512
  2. Payload

    ​ 有效载荷,载荷就是存储有效信息的地方,也就是存储我们自己信息的地方。它包含三个内容,分别是标准中注册的声明,公共的声明,私有声明,也就是安全分类,类似与java的访问权限修饰符。

    {
        "name": 'wp',
        "age": '18',
        "amin":true
    }
    
  3. Signature

    ​ 第三部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret),该密钥仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名

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

    在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象。

    var encodedstring = base64UrlEncode(header) + '.' +base64urlEncode(payload);
    var signature = HMACSHA256(encodedString, 'secret');
    

    ​ 那么Application Server如何进行验证呢?可以利用JWT前两段,用同一套哈希算法和同一个secret计算一个签名值,然后把计算出来的签名值和收到的JWT第三段进行比较,如果相同则认证通过。

实践JWT

JWT认证关键在于生成JWT,和解析JWT这两个地方。

生成JWT、

    String signature = "admin";
    private long time = 1000*60*60*24;
    @Test
    public void jwt(){
        //生成JWT
        JwtBuilder builder = Jwts.builder();
        String jwtToken = builder
                //header头部
                .setHeaderParam("type", "JWT")
                .setHeaderParam("alg", "HS256")
                //payload有效载荷
                .claim("username", "wp")
                .claim("role", "admin")
                .setSubject("admin-test")
                .setExpiration(new Date(System.currentTimeMillis()+time))  //设置有效期,为一天
                .setId(UUID.randomUUID().toString())
                //signature签名
                .signWith(SignatureAlgorithm.HS256,signature)
                .compact();
        System.out.println(jwtToken);
    }

运行结果

eyJ0eXBlIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJ1c2VybmFtZSI6IndwIiwicm9sZSI6ImFkbWluIiwic3ViIjoiYWRtaW4tdGVzdCIsImV4cCI6MTY1MzgyMDQ5MywianRpIjoiMTYxMGMzMjQtMzVhNy00NjMzLTk3MmItOWVkYzQyNjA3Zjk4In0._4xmfhg_C5WVzk1rWF66iG2knr5nSxyUDgEytlTg394

解析JWT

    @Test
    public void parse(){
        //解析JWT
        String jwtToken = "eyJ0eXBlIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJ1c2VybmFtZSI6IndwIiwicm9sZSI6ImFkbWluIiwic3ViIjoiYWRtaW4tdGVzdCIsImV4cCI6MTY1MzcxMTI5OSwianRpIjoiMWIxYjE0OTMtNmVlMy00YTZhLTkzNTUtMDdlOTJkNzhiNDk3In0.dtXMVzKt4zrfO5ml8KVV0bsHTJ4QxXEXf2YJ7RQ3ECs";
        JwtParser jwtParser = Jwts.parser();
        Jws<Claims> claimsJws = jwtParser.setSigningKey(signature).parseClaimsJws(jwtToken);
        Claims body = claimsJws.getBody();
        System.out.println(body.getSubject());
        System.out.println(body.get("username"));
        System.out.println(body.get("role"));
        System.out.println(body.getId());
        System.out.println(body.getExpiration());
    }

运行结果

admin-test
wp
admin
1610c324-35a7-4633-972b-9edc42607f98
Sun May 29 18:34:53 CST 2022

java中的运用

Springboot中的运用

引入依赖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值