前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot集成JWT详细步骤

SpringBoot集成JWT详细步骤

作者头像
Blue_007
发布2023-10-21 12:38:11
4570
发布2023-10-21 12:38:11
举报
文章被收录于专栏:代码生涯

1、JWT 简介

JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地将信息作为JSON对象传输。由于此信息是经过数字签名的,因此可以进行验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名

2、应用场景

  • 授权:这是使用JWT最常见的方案。当用户登录后,每个后续请求将会在header带上JWT,允许用户访问允许使用该令牌的路由、服务和资源。单点登录是当今广泛使用JWT的一个特性,因为它具有较小的开销和易于跨不同域使用的能力。
  • 信息交换:JWT是保证各方之间安全地传输信息的一种好方法。因为JWT可以被签名,例如使用公钥/私钥对,可以确保发件人是他们所说的人。另外,由于使用header和playload计算签名,还可以验证内容是否被篡改。

3、Jwt结构

JSON Web令牌以紧凑的形式由三部分组成,这些部分由点(.)分隔,分别是:

  • 标头
  • 有效载荷
  • 签名 因此,JWT通常如下所示:

xxxxx.yyyyy.zzzzz

4、Jwt工作流程

  1. 用户使用账号登录发出post请求;
  2. 服务器使用私钥创建一个jwt;
  3. 服务器返回这个jwt给浏览器;
  4. 浏览器将该jwt串放在请求头中,向服务器发送请求;
  5. 服务器验证该jwt;
  6. 返回响应的资源给浏览器。

5、SpringBoot 集成 Jwt

  • 工作目录介绍
  • pom配置文件
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.example</groupId>
  <artifactId>loginintercept</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>loginintercept</name>
  <description>Demo project for Spring Boot</description>

  <properties>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--  jwt   -->
    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.7.0</version>
    </dependency>

    <!-- lombok   -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>
  • JwtUtils 工具类
代码语言:javascript
复制
package com.example.loginintercept.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import org.springframework.stereotype.Component;

/**
 * token 工具类
 */
@Component
public class JwtUtils {

  // 过期时间
  private static long expire = 604800;
  // 秘钥
  private static String secret = "HSyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9";

  /**
   * 创建一个token
   *
   * @param userId
   * @return
   */
  public String generateToken(String userId) {
    Date now = new Date();
    Date expireDate = new Date(now.getTime() + expire);
    return Jwts.builder().setHeaderParam("type", "JWT").setSubject(userId).setIssuedAt(now)
        .setExpiration(expireDate).signWith(
            SignatureAlgorithm.HS512, secret).compact();
  }

  /**
   * 解析token
   */
  public Claims getClaimsByToken(String token) {
    try {
      return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    } catch (Exception e) {
      System.out.println("validate is token error");
      return null;
    }
  }

  /**
   * 判断 token 是否过期
   */
  public boolean isTokenExpired(Date expiration){
    return expiration.before(new Date());
  }

}
  • ResultT 统一返回格式
代码语言:javascript
复制
package com.example.loginintercept.config;

import java.util.HashMap;
import java.util.Map;

public class ResultT extends HashMap<String, Object> {

  public ResultT() {
    put("code", 0);
    put("msg", "success");
  }

  public static ResultT ok() {
    ResultT t = new ResultT();
    t.put("msg", "操作成功");
    return t;
  }

  public static ResultT ok(String msg) {
    ResultT t = new ResultT();
    t.put("msg", msg);
    return t;
  }

  public static ResultT ok(Map<String, Object> map) {
    ResultT t = new ResultT();
    t.putAll(map);
    return t;
  }

  public static ResultT error(int code, String msg) {

    ResultT t = new ResultT();
    t.put("code", code);
    t.put("msg", msg);
    return t;
  }

  public static ResultT error() {
    return error(500, "未知异常");
  }

  public ResultT put(String key, Object value){
    super.put(key, value);
    return this;
  }
}
  • TokenInterceptor 创建一个token拦截器
代码语言:javascript
复制
package com.example.loginintercept.config;

import com.example.loginintercept.exception.TokenRuntimeException;
import io.jsonwebtoken.Claims;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 * 创建一个 token 拦截器.
 * 需要继承 HandlerInterceptorAdapter,并且声明为spring的组件
 */
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {

  // 注入jwt工具类
  @Autowired
  private JwtUtils jwtUtils;

  // 重写 前置拦截方法
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    // 1、从请求头中获取token
    String token = request.getHeader("token");

    // 2、判断 token 是否存在
    if (token == null ||"".equals(token)) {
      System.out.println("未登录");
      // 这里可以自定义 抛出 token 异常
      throw new TokenRuntimeException("未登录");
    }

    // 3、解析token
    Claims claim = jwtUtils.getClaimsByToken(token);

    if (null == claim) {
      System.out.println("token 解析错误");
      // 这里可以自定义 抛出 token 异常
      throw  new TokenRuntimeException("token 解析错误");
    }

    // 4、判断 token 是否过期
    Date expiration = claim.getExpiration();
    boolean tokenExpired = jwtUtils.isTokenExpired(expiration);
    if (tokenExpired) {
      System.out.println("token已过期,请重新登录");
      // 这里可以自定义 抛出 token 异常
      throw new TokenRuntimeException("token已过期,请重新登录");
    }

    // 5、 从 token 中获取员工信息
    String subject = claim.getSubject();

    // 6、去数据库中匹配 id 是否存在 (这里直接写死了)
    if (null == subject) {
      System.out.println("员工不存在");
      // 这里可以自定义 抛出 token 异常
      throw new TokenRuntimeException("员工不存在");
    }

    // 7、成功后 设置想设置的属性,比如员工姓名
    request.setAttribute("userId", subject);
    request.setAttribute("userName", "张三");

    return true;
  }
}
  • WebMvcConfig 设置拦截器
代码语言:javascript
复制
package com.example.loginintercept.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 设置拦截器.
 * 打上configuration 注解,标注为配置项
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

  // 注入 token 拦截器
  @Autowired
  private TokenInterceptor interceptor;

  /**
   * 重写添加拦截器
   */
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    // 添加自定义拦截器,并拦截对应 url
    registry.addInterceptor(interceptor).addPathPatterns("/gateway/**");
  }
}
  • GatewayController 模仿需要登录后才能访问的资源
代码语言:javascript
复制
package com.example.loginintercept.controller;

import com.example.loginintercept.config.ResultT;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * controller
 */
@RestController
@RequestMapping("/gateway")
public class GatewayController {

  @RequestMapping(value = "/find",method = RequestMethod.POST)
  public ResultT find(){
    return ResultT.ok("find one success");
  }

}
  • LoginController 登录
代码语言:javascript
复制
package com.example.loginintercept.controller;

import com.example.loginintercept.config.JwtUtils;
import com.example.loginintercept.config.ResultT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * 登录
 */
@RestController
public class LoginController {
  // 注入jwt工具类
  @Autowired
  private JwtUtils jwtUtils;
  @RequestMapping(value = "/login", method = RequestMethod.POST)
  public ResultT login(String name, String psw) {
    String userId = "132";
    String token = jwtUtils.generateToken(userId);
    return ResultT.ok().put("token", token);
  }
}
  • SysRuntimeExceptionHandler 全局异常处理
代码语言:javascript
复制
package com.example.loginintercept.exception;

import com.example.loginintercept.config.ResultT;
import com.example.loginintercept.exception.TokenRuntimeException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理
 */
@RestControllerAdvice
public class SysRuntimeExceptionHandler {

  @ExceptionHandler(TokenRuntimeException.class)
  public ResultT tokenRuntimeException(TokenRuntimeException e) {
    e.printStackTrace();
    return ResultT.error(e.getCode(), e.getMsg());
  }

  @ExceptionHandler(Exception.class)
  public ResultT handlerException(Exception e){
    e.printStackTrace();
    return ResultT.error();
  }
}
  • TokenRuntimeException 自定义 token 异常
代码语言:javascript
复制
package com.example.loginintercept.exception;

import lombok.Data;

/**
 * 自定义 token 异常
 */
@Data
public class TokenRuntimeException extends RuntimeException{

  private Integer code = 401;
  private String msg;

  public TokenRuntimeException(String msg) {
    this.msg = msg;
  }

}

6、运行测试

  • 首先访问需要 token 的资源
  • 登录 获取 token
  • 请求头中存放 token 再次访问需要 token 的资源

当然,一般在实际开发中请求头会叫Authorizationtoken 内容的前面通常会拼接上 `’Bearer ‘ 这里为了方便,就不这样做了

好,完全!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-10-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 代码生涯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、JWT 简介
  • 2、应用场景
  • 3、Jwt结构
  • 4、Jwt工作流程
  • 5、SpringBoot 集成 Jwt
  • 6、运行测试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档