什么是JWT?JWT能做什么?JWT怎么使用?

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 什么是JWT?
  • JWT能做什么?
  • JWT 结构
  • 信息安全问题
  • SpringBoot应用中如何使用JWT?
    • 1.添加依赖
    • 2.拦截器配置(受限请求访问,检验Token)


什么是JWT?

  • JWT 全称JSON Web Token ,简单的说就是用户登录成功之后,将用户的信息进行加密,然后生成一个token 返回给客户端

什么是JWT?JWT能做什么?JWT怎么使用?_第1张图片

JWT能做什么?

  • 授权:这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,从而允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小并且能够在不同的域中轻松使用。
  • 信息交换:JSON Web 令牌是在各方之间安全传输信息的好方法。因为可以对 JWT 进行签名(例如,使用公钥/私钥对),所以可以确定发件人就是他们所说的那个人。此外,由于使用标头和有效负载计算签名,还可以验证内容没有被篡改。

JWT 结构

什么是JWT?JWT能做什么?JWT怎么使用?_第2张图片

JWT 由以点 ( .) 分隔的三部分组成,分别是:

  • 标头(HEADER)
  • 有效载荷(PAYLOAD)
  • 签名(SIGNATURE)

因此,JWT 通常如下所示:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • 标头(HEADER)
    • 标头通常由两部分组成:令牌的类型,即 JWT,以及正在使用的签名算法,例如 HMAC SHA256 或 RSA,并使用 Base64编码组成 JWT 结构的 第一部分
  • 有效载荷(PAYLOAD)
    • 令牌的第二部分是有效负载,其中包含声明。声明是关于实体(通常是用户)和附加数据的声明,并使用 Base64编码组成 JWT 结构的 第二部分
  • 签名(SIGNATURE)
    • 要创建签名部分,必须获取编码后标头(HEADER)有效载荷(PAYLOAD)以及我们提供的一个密钥,然后使用标头(HEADER)中指定的签名算法进行签名。
    • 签名的作用是保证 JWT 没有被篡改过

信息安全问题

  • Base64是一种编码,是可逆的,所以,在JWT中,不应该在有效载荷(PAYLOAD)里面加入任何敏感的数据(如:密码)

SpringBoot应用中如何使用JWT?

1.添加依赖


<dependency>
    <groupId>com.auth0groupId>
    <artifactId>java-jwtartifactId>
    <version>3.19.1version>
dependency>
  • 生成令牌/解析令牌(签名算法和密钥要相同,否则会抛异常)案例
public static void main(String[] args) {
      // 生成令牌
      Map<String, Object> map = new HashMap<>();
      map.put("id", 100);
      map.put("name", "李四");
      String token = JWT.create()
              .withClaim("userId", 1) // 设置payload
              .withClaim("userName", "张三")
              .withClaim("user", map) // 自定义Map
              .withIssuedAt(new Date()) // 令牌生成时间
              .withExpiresAt(new Date(System.currentTimeMillis() + 8 * 60 * 60 * 1000)) // 令牌过期时间(8小时有效)
              .sign(Algorithm.HMAC256("Test666"));// 设置签名算法和密钥
      System.out.println("token = " + token);

      // 解析令牌
      JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("Test666")).build();
      DecodedJWT verify = jwtVerifier.verify(token);
      System.out.println("解析结果:" + verify.getClaim("userId").asInt());
      System.out.println("解析结果:" + verify.getClaim("userName").asString());
      System.out.println("解析结果:" + verify.getClaim("user").asMap());
      System.out.println("解析结果:" + verify.getClaim("user").asMap().get("id"));
      System.out.println("解析结果:" + verify.getClaim("user").asMap().get("name"));
  }

2.拦截器配置(受限请求访问,检验Token)

  • 创建返回值对象VO
/**
 * 公用返回结果Vo
 * @Date: 2022/04/14 22:01
 * @Version 1.0
 **/
@ApiModel(value = "ResultVo", description = "公用返回结果Vo")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResultVo {

    @ApiModelProperty(value = "状态码", example = "200")
    private Integer code;

    @ApiModelProperty(value = "返回信息", example = "success")
    private String msg;

    @ApiModelProperty(value = "返回数据", example = "data")
    private Object data;
}
  • 创建拦截器,新建TokenInterceptor

    • 认证成功后生成的token,前端通过请求头传递token给后端

      axios({
      	method: 'get',
      	url: url,
      	headers: {
      		token: this.token
      	}
      }).then(res => {
      	console.log(data);
      });
      
  • 由于浏览器的预检机制:当前端发送的请求携带自定义配置时,浏览器会发送一次预检请求(method = “OPTIONS”),如果后端正常响应,再发送第二次请求提交数据

import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.cy.fmmall.common.vo.ResultStatus;
import com.cy.fmmall.common.vo.ResultVo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 定义拦截器
 * @Date: 2022/04/14 12:26
 * @Version 1.0
 **/
@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {

        // System.out.println("------这不就进来了吗");

        // 处理 method = “OPTIONS” 预检请求
        String requestMethod = request.getMethod();
        if ("OPTIONS".equalsIgnoreCase(requestMethod)){
            return true;
        }
        
        if (StrUtil.isBlank(token)){
            ResultVo resultVo = new ResultVo(ResultStatus.SUCCESS, "请先登录", null);
            doResponse(response, resultVo);
            return false;
        } else {
            try {
                // 解析令牌
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("Lidasha521")).build();
                jwtVerifier.verify(token);
                /*
                 * 常见异常
                 * SignatureVerificationException: 签名不一致异常
                 * TokenExpiredException: 令牌过期异常
                 * AlgorithmMismatchException: 算法不匹配异常
                 */
            } catch (SignatureVerificationException | AlgorithmMismatchException e) {
                ResultVo resultVo = new ResultVo(ResultStatus.FAIL, "Token不合法,请重新登录", null);
                doResponse(response, resultVo);
            } catch (TokenExpiredException e) {
                ResultVo resultVo = new ResultVo(ResultStatus.FAIL, "Token已过期,请重新登录", null);
                doResponse(response, resultVo);
            } catch (Exception e){
                ResultVo resultVo = new ResultVo(ResultStatus.FAIL, "请先登录", null);
                doResponse(response, resultVo);
            }
            return true;
        }
    }

    // 创建json格式返回对象
    private void doResponse(HttpServletResponse response, ResultVo resultVo) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        PrintWriter writer = response.getWriter();
        String msg = new ObjectMapper().writeValueAsString(resultVo);
        writer.println(msg);
        writer.flush();
        writer.close();
    }
}
  • 配置拦截器:新建InterceptorConfig
import com.cy.fmmall.api.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;

/**
 * 配置拦截器
 * @Date: 2022/04/14 12:22
 * @Version 1.0
 **/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Resource
    private TokenInterceptor tokenInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(tokenInterceptor);
        // 设置需要拦截的请求(也可设置成list集合)
        interceptorRegistration.addPathPatterns("/product/**");
        // 设置不需要拦截的请求(也可设置成list集合)
        interceptorRegistration.excludePathPatterns("/user/**");
    }
}
  • 测试结果

什么是JWT?JWT能做什么?JWT怎么使用?_第3张图片

你可能感兴趣的:(JWT,SpringBoot,java,spring,boot,idea)