JWT令牌,过滤器,拦截器

144 阅读5分钟

一.JWT令牌

令牌就是用户身份的标识,其本质就是一个字符串。令牌的形式有很多,我们使用的是功能强大的JWT令牌

1.JWT令牌介绍

JWT:Json Web Token,定义了一种简介的自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。

  • 简介:是指jwt就是一个简单的字符串,可以在请求参数或者是请求头当中直接传递
  • 自包含:指的是jwt令牌,看似是一个随机的字符串,但是我们是可以根据自身的需求在jwt令牌中存储自定义的数据内容。如:可以直接在jwt令牌中存储用户的相关信息。
  • 简单来讲,jwt就是将原始的json数据格式进行了安全的封装,这样就可以直接基于jwt在通信双方安全的进行信息传输了。

组成:

  1. 头:算法,类型 Header:记录令牌类型,签名算法等,例如:{"alg":"HS256","type":"JWT"}
  2. 载荷:个人非敏感数据,内置数据 Payload,携带一些自定义信息,默认信息等。例如:{"id":"1":"username":"Tom"}
  3. 签名:Base64(头,载荷,密钥) Signature:防止Token被篡改,确保安全性。将header,payload加入指定密钥,通过指定签名算法计算而来。密钥不能暴露,否则就不安全了。正是因为jwt令牌最后一个部分数字签名的存在,所以整个jwt令牌是非常安全可靠的,一旦jwt令牌当中任何一个部分,任何一个字符被篡改了,整个令牌在校验的时候都会失败,所以它非常安全。

image.png

JWT是如何将原始的JSON格式数据,转变为字符串的呢?

  • 其实在生成JWT令牌时,会对JSON格式的数据进行一次编码,进行base64编码
  • Base64:是一种基于64个可打印的字符来表示二进制数据的编码方式。既然能编码,那就可以解码,所使用的64个字符分别是A-Z,a-z,0-9,+,/。加起来就是64个字符。任何数据经过base64编码之后,最终就会通过64个字符来表示。还有一个符号 = ,等号它是一个补位的符号。
  • 需要注意的是Base64是编码方式,不是加密方式。

2.生成和校验

  1. 首先我们先来实现JWT令牌的生成,想要使用JWT令牌需要先引入依赖
<!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

在引入完JWT依赖后,就可以调用工具包中提供的API来完成JWT令牌的生成和校验。工具类:Jwts 2.生成JWT代码实现

package com.itheima.boottlias.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;

public class JwtUtils {

    private static String signKey = "SVRIRUlNQQ==";
    private static Long expire = 24 * 60 * 60 * 1000L;

    /**
     * 生成JWT令牌
     * @return
     */
    public static String generateJwt(Map<String,Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }

运行后得出的就是令牌

eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk

我们可以将生成的令牌复制一下,然后打开JWT的官网,将生成的令牌直接放在Encode位置,此时就会自动将令牌解析出来。

image.png 第一部分解析出来,看到JSON格式的原始数据,所使用的签名算法为HS256。

第二部分是我们自定义的数据之前我们自定义的数据就是id,还有一个exp代表的是我们所设置的过期时间。 由于前两个部分是base64编码,所以是可以直接解码出来的,但是最后一个部分并不是base64编码,是经过签名算法算出来的,所以最后一个部分是不会解析的。 3.实现了JWT令牌的生成,下面我们接着使用Java代码来校验JWT令牌(解析生成的令牌)

@Test
public void testParseJwt() {
    Claims claims = Jwts.parser().setSigningKey("aXRjYXN0")
        .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAsInVzZXJuYW1lIjoiaXRoZWltYSIsImV4cCI6MTcwMTkwOTAxNX0.N-MD6DmoeIIY5lB5z73UFLN9u7veppx1K5_N_jS9Yko")
        .getBody();
    System.out.println(claims);
}

令牌解析后,我们可以看到id和过期时间,如果在解析的过程中没有报错,就说明解析成功了。

{id=10, username=itheima, exp=1701909015}

下面我们做一个测试:把令牌header中的数字9变为8,运行测试方法后发现报错:

image.png 结论:篡改令牌中的任何一个字符,在对令牌进行解析时都会报错,所以JWT令牌是非常安全可靠的。

把令牌的过期时间修改为1分钟

@Test
public void genJwt(){
    Map<String, Object> claims = new HashMap<>();
    claims.put("id", 10);
    claims.put("username", "itheima");

    String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, "aXRjYXN0")
        .addClaims(claims)
        .setExpiration(new Date(System.currentTimeMillis() + 60 * 1000)) //有效期60s
        .compact();
    System.out.println(jwt);
    //输出结果:eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjczMDA5NzU0fQ.RcVIR65AkGiax-ID6FjW60eLFH3tPTKdoK7UtE4A1ro
}

@Test
public void parseJwt(){
    Claims claims = Jwts.parser()
        .setSigningKey("aXRjYXN0")//指定签名密钥
        .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjczMDA5NzU0fQ.RcVIR65AkGiax-ID6FjW60eLFH3tPTKdoK7UtE4A1ro")
        .getBody();

    System.out.println(claims);
}

等待1分钟之后运行测试方法发现也报错了,说明:JWT令牌过期后,令牌就失效了,解析的为非法令牌。

接着我们就需要在案例当中通过JWT令牌技术来跟踪会话,具体的思路我们前面已经分析过了, 1.生成令牌 在登录成功之后来生成一个JWT令牌,并且把这个令牌直接返回给前端 2.校验令牌 拦截前端请求,从请求中获取到令牌,对令牌进行校验解析。

二.过滤器Filter

  • Filter表示过滤器,是JavaWeb三大组件(Servlet,Filter,Listener)之一。
  • 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
  • 过滤器一般完成一些通用的操作,比如:登录校验,统一编码处理,敏感字符处理

三.拦截器Interceptor

在拦截器当中,我们通常也是做一些通用性的操作,比如:我们可以通过拦截器来拦截前端发起的请求,将登录校验的逻辑全部编写在拦截器当中。在校验的过程当中,如发现用户登录了(携带JWT令牌且是合法令牌),就可以直接放行,去访问spring当中的资源。如果校验时发现并没有登录或是非法令牌,就可以直接给前端响应未登录的错误信息。

下面我们通过快速入门程序,来学习下拦截器的基本使用。拦截器的使用步骤和过滤器类似,也分为两步:

  1. 定义拦截器
  2. 注册配置拦截器