【Spring Boot】JWT 整合到 Spring Boot

文章目录

前言

本文,我们将 JWT 整合到 Spring Boot 中。

JWT 是什么?

JWT -> JSON Web Token

JWT 是一种开放标准(RFC 7519),用于在网络应用中传递声明信息。

JWT 到一大优势是它的可扩展性和自包含性。它可以在各个系统之间进行安全传输和验证,因为它包含了所有必要的信息,并且经过签名保证了数据的完整性。JWT 通常用于身份验证和授权机制,比如 Web 应用中通过 JWT 来验证用于的身份,下面我们就来实践一下。

本文的实践案例,基于本人之前的文章 Spring Boot 整合 Swagger 接口文档工具项目。

安装依赖

安装 JWT 依赖:

xml 复制代码
<!-- 引入jwt-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.8.2</version>
</dependency>

添加 JWT 处理方法

我们这里使用用户字段 id,name 和 password 来创建 token。

1. 生成 token

java 复制代码
// 生成 token
public static String createToken(User user) {
    Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
    Map<String, Object> map = new HashMap<>();
    map.put("alg", "HS256");
    map.put("typ", "JWT");
    System.out.println(user+"user");
    String token = JWT.create()
            .withClaim("id", user.getId())
            .withClaim("name", user.getName())
            .withClaim("password", user.getPassword())
            .withExpiresAt(expireDate)
            .withIssuedAt(new Date())
            .sign(Algorithm.HMAC256(SECRET));

    return token;
}

2. 验证 token

java 复制代码
// 校验 token
public static Map<String, Claim> verifyToken(String token) {
    DecodedJWT jwt = null;
    try {
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
        jwt = verifier.verify(token);
    } catch (Exception e) {
        return null;
    }
    return jwt.getClaims();
}

完整的代码如下。我们在包 com.launch.util 下创建 JwtTokenUtils.java 文件:

java 复制代码
package com.launch.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.launch.model.User;

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

public class JwtTokenUtils {

    private static final String SECRET = "jwtSECRET"; // 密钥

    private static final long EXPIRATION = 3600L; // 3600 秒

    // 生成 token
    public static String createToken(User user) {
        Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");
        System.out.println(user+"user");
        String token = JWT.create()
                .withClaim("id", user.getId())
                .withClaim("name", user.getName())
                .withClaim("password", user.getPassword())
                .withExpiresAt(expireDate)
                .withIssuedAt(new Date())
                .sign(Algorithm.HMAC256(SECRET));

        return token;
    }

    // 校验 token
    public static Map<String, Claim> verifyToken(String token) {
        DecodedJWT jwt = null;
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
            jwt = verifier.verify(token);
        } catch (Exception e) {
            return null;
        }
        return jwt.getClaims();
    }
}

创建过滤文件

我们紧接着创建拦截的过滤文件。我们在包 com.launch.config.authenticationhandler.jwt 下创建文件 JwtFilter.java:

java 复制代码
package com.launch.config.authenticationhandler.jwt;

import com.auth0.jwt.interfaces.Claim;
import com.launch.util.JwtTokenUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

// /api/user/secure/* 的文件起作用
@WebFilter(filterName = "JwtFilter", urlPatterns = "/api/user/secure/*") 
public class JwtFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;

        response.setCharacterEncoding("UTF-8");

        final String token = request.getHeader("Auth");
 
        if("OPTIONS".equals(request.getMethod())) { // 是否是 OPTIONS 请求
            response.setStatus(HttpServletResponse.SC_OK);
            chain.doFilter(request, response);
        }
        else {

            if(token == null) {
                response.getWriter().write("no token");
                return;
            }
            Map<String, Claim> userData = JwtTokenUtils.verifyToken(token); // 检验 token
            if(userData == null) {
                response.getWriter().write("token illegal");
                return;
            }

            Integer id = userData.get("id").asInt(); // 获取 id
            String userName = userData.get("name").asString(); // 获取用户名
            String password = userData.get("password").asString(); // 获取密码
            request.setAttribute("id", id); // 设置 id
            request.setAttribute("username", userName); // 设置用户名
            request.setAttribute("password", password); // 设置密码

            chain.doFilter(req, res); // 过滤成功
        }
    }

    @Override
    public  void destroy() {}
}

这个文件做了什么事情呢?
urlPatterns = "/api/user/secure/*" 表示接口 /api/user/secure/* 下的接口需要经过验证才可以访问。
request.getHeader("Auth") 我们通过 request 请求的 header 中获取提前存在字段 Auth 上的 token 值。在真实登陆的时候就会存储进去 Auth 内。
request.setAttribute("id", id); 将获取的 id 值存在 request 请求上。usernamepassword 同理。

为了过滤器能够生效,我们需要在入口类添加注释

java 复制代码
@ServletComponentScan(basePackages="com.launch.config.authenticationhandler.jwt")。

验证

最后,我们验证下 token 是否集成生效。该操作在包 com.lauch.controller 下 UserController.java 文件中操作。

我们模拟登陆,生成 token:

java 复制代码
// 用户登陆
@GetMapping("/login")
String login(User user) {
    // jimmy simulation
    //        {
    //            "id": 2,
    //            "name": "Jimmy",
    //            "age": 18,
    //            "password": "123456"
    //        }
    String token = JwtTokenUtils.createToken(user);
    return token;
}

下面我们来获取当前登陆的用户信息。

java 复制代码
@GetMapping("/secure/current_registrant")
public String currentRegistrant(HttpServletRequest request) {
    Integer id = (Integer) request.getAttribute("id");
    String username = request.getAttribute("username").toString();
    String password = request.getAttribute("password").toString();
    return "当前用户信息: id="+id+" ,username="+username+" ,password="+password;
}

上面也提及到了,我们是要获取 request 中 header 上 Auth 的值。这里就有 Auth = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsIm5hbWUiOiJKaW1teSIsImlkIjoyLCJleHAiOjE2OTExNjA0NjMsImlhdCI6MTY5MTE1Njg2M30.nIP23eI-vgIZC-Fw2FGDQw1GrXlA4mBVBb3vkSFySgc。我们在 postman 上设置,并请求该接口:

参考

  • JSON Web Token 入门教程
  • 五分钟带你了解啥是JWT
  • SpringBoot 快速集成 JWT 实现用户登录认证
  • 《Spring Boot 实战派》
相关推荐
皮皮林55111 小时前
拒绝写重复代码,试试这套开源的 SpringBoot 组件,效率翻倍~
java·spring boot
用户908324602733 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840824 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解4 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解4 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记4 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者5 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840825 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解5 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者6 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq