JWT令牌——详解

目录

一、JWT是什么?

二、JWT的组成

三、JWT应用场景

四、生成和校验JWT令牌

五、具体应用


一、JWT是什么?

        在当今的Web开发中,安全认证和授权变得尤为重要。JWT(JSON Web Tokens)是一种开放标准(RFC 7519),它定义了一种简洁、紧凑且自包含的方式,用于在各方之间安全地传输信息。这种信息可以被验证和信任,但不需要依赖服务器来解析。

        JWT全称:JSON Web Token (官网:JSON Web Tokens - jwt.ioJSON Web Tokens - jwt.ioJSON Web Tokens - jwt.io)      

  • 简洁:是指jwt就是一个简单的字符串。可以在请求参数或者是请求头当中直接传递。
  • 紧凑:指的是它能够以较小的体积包含所有的信息。JWT使用Base64Url编码算法JWT支持多种加密算法,如HS256、RS256等,这些算法能够在保证安全性的同时,保持令牌的大小不会过大。
  • 自包含:指的是jwt令牌,看似是一个随机的字符串,但是我们是可以根据自身的需求在jwt令牌中存储自定义的数据内容。JWT包含了所有必要的信息来验证其有效性,不需要额外的查询或数据库操作。如:可以直接在jwt令牌中存储用户的相关信息。

        简单来讲,jwt就是将原始的json数据格式进行了安全的封装,这样就可以直接基于jwt在通信双方安全的进行信息传输了。

二、JWT的组成

        JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)用.分隔。这三部分以.分隔,形如xxxxx.yyyyy.zzzzz

  假设我们有一个JWT如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

头部(Header):包含了令牌的类型和所使用的加密算法。

  • 编码后:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
  • 解码后:
    {
      "alg": "HS256",
      "typ": "JWT"
    }
    头部告诉我们签名算法(HS256)和令牌类型(JWT)

载荷(Payload):存放实际的数据,可以是任何形式的JSON对象。

  • 编码后:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
  • 解码后:
    {
      "name": "John Doe",
      "admin": true,
      "roles": ["user", "admin"],
      "exp": 1687016741
    }

    载荷包含了声明信息,如用户姓名、是否为管理员、用户角色和过期时间。

签名(Signature):用于验证令牌的发送者和内容是否被篡改。签名由头部、载荷、秘钥和指定的算法生成。       

  • SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • 签名是对头部和载荷的加密,确保令牌的完整性和可靠性。

        在生成JWT令牌时,会对JSON格式的数据进行一次编码:进行base64编码

        Base64:是一种基于64个可打印的字符来表示二进制数据的编码方式。既然能编码,那也就意味着也能解码。所使用的64个字符分别是A到Z、a到z、 0- 9,一个加号,一个斜杠,加起来就是64个字符。任何数据经过base64编码之后,最终就会通过这64个字符来表示。当然还有一个符号,那就是等号。等号它是一个补位的符号。需要注意的是Base64是编码方式,而不是加密方式。

三、JWT应用场景

        JWT令牌最典型的应用场景就是登录认证:

  1. 客户端请求认证:用户通过用户名和密码请求登录。
  2. 服务器验证请求:服务器验证用户凭证,如果有效,生成JWT。
  3. 客户端存储令牌:客户端接收到JWT后,存储在本地(通常是LocalStorage或SessionStorage)。
  4. 客户端发送令牌:之后的每个请求都带有这个JWT,通常放在HTTP头的Authorization字段中。
  5. 服务器验证令牌:服务器验证JWT的有效性,如果有效则处理请求,否则拒绝。

四、生成和校验JWT令牌

要想使用JWT令牌,需要先引入JWT的依赖:

<!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

在引入完JWT来赖后,就可以调用工具包中提供的API来完成JWT令牌的生成和校验。

生成JWT代码实现:

@Test
public void genJwt(){
    Map<String,Object> claims = new HashMap<>();
    claims.put("id",1);
    claims.put("username","Tom");
    
    String jwt = Jwts.builder()
        .setClaims(claims) //自定义内容(载荷)          
        .signWith(SignatureAlgorithm.HS256, "itheima") //签名算法        
        .setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期一天   
        .compact();//将JWT转换为一个紧凑的字符串形式
    
    System.out.println(jwt);
}

       运行测试方法,输出的结果就是生成的JWT令牌,,通过英文的点分割对三个部分进行分割,我们可以将生成的令牌复制一下,然后打开JWT的官网,将生成的令牌直接放在Encoded位置,此时就会自动的将令牌解析出来。   

        前两个部分是base64编码,所以是可以直接解码出来。但最后一个部分并不是base64编码,是经过签名算法计算出来的,所以最后一个部分是不会解析的。

        实现了JWT令牌的生成,下面我们接着使用Java代码来校验JWT令牌(解析生成的令牌):

@Test//单元测试注解
public void parseJwt(){
    Claims claims = Jwts.parser()
        .setSigningKey("itheima")//指定签名密钥(必须保证和生成令牌时使用相同的签名密钥)  
	    .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk")
        .getBody();//获取的是第二部分

    System.out.println(claims);
}

         篡改令牌中的任何一个字符,在对令牌进行解析时都会报错,所以JWT令牌是非常安全可靠的。JWT令牌过期后,令牌就失效了,解析的为非法令牌。 JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。

五、具体应用

       JWT令牌的生成和校验的基本操作我们已经学习完了,接下来我们就需要在案例当中通过JWT令牌技术来跟踪会话。

        在项目工程下创建utils包,创建JWT工具类(生成令牌+解析令牌)

public class JwtUtils {

    private static String signKey = "itheima";//签名密钥
    private static Long expire = 43200000L; //有效时间

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @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;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)//指定签名密钥
                .parseClaimsJws(jwt)//指定令牌Token
                .getBody();
        return claims;
    }
}

        登录完成后,调用工具类生成JWT令牌并返回  

@RestController
@Slf4j
public class LoginController {
    //依赖业务层对象
    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) {
        //调用业务层:登录功能
        Emp loginEmp = empService.login(emp);

        //判断:登录用户是否存在
        if(loginEmp !=null ){
            //自定义信息
            Map<String , Object> claims = new HashMap<>();
            claims.put("id", loginEmp.getId());
            claims.put("username",loginEmp.getUsername());
            claims.put("name",loginEmp.getName());

            //使用JWT工具类,生成身份令牌
            String token = JwtUtils.generateJwt(claims);
            return Result.success(token);
        }
        return Result.error("用户名或密码错误");
    }
}

       用户登录成功后,系统会自动下发JWT令牌,然后在后续的每次请求中,都需要在请求头header中携带到服务端,请求头的名称为 token ,值为 登录时下发的JWT令牌。如果检测到用户未登录,则会返回固定错误信息。

       服务端需要统一拦截所有的请求,从而判断是否携带的有合法的JWT令牌。那怎么样来统一拦截到所有的请求校验令牌的有效性呢?这里我们会用到两种解决方案:Filter过滤器和Interceptor拦截器。


        JWT虽然提供了一种简洁、安全的认证方式,但也有一些注意事项,如令牌的有效期设置、敏感信息的存储等。在使用JWT时,应结合具体的应用场景和需求进行合理的设计和配置。总之,JWT是现代Web应用中一个非常有用的工具,它简化了认证流程,提高了应用的安全性和可扩展性。通过理解其原理和正确的使用方法,你可以有效地保护你的应用和用户数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值