SpringBoot集成JWT快速入门Demo

目录

1. 概述

2. JWT的请求流程

3. Session认证与JWT认证的区别 

4 JWT优缺点

4.1 优点

4.2 缺点

5. 快速入门

5.1 创建工程

5.2 导入依赖

5.3 添加配置文件

5.4 添加Swagger2配置类

5.5 添加JWT工具类

5.6 添加entity、service、controller类

5.7 添加拦截器类

5.8 添加拦截器配置文件

5.9 创建启动类

5.10 测试

5.10.1 通过swaggler测试  

5.10.2 通过Postman测试

6. Demo下载地址


1. 概述

        近年来,随着前后端分离、微服务等架构的兴起,传统的cookie+session身份验证模式已经逐渐被基于Token的身份验证模式取代。

备注:将token或者一个唯一标识UUID=UUID.randomUUID().toString()存进Cookie中(别存在Http的header中了),设置路径为整个项目根路径/*; 往往以这个唯一标识为key,用户信息为value缓存在服务器中,实现单点登录

        JWT (Json web token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519)。它定义了一种紧凑的,自包含的方式,用于通信双方之间以JSON对象的形式安全传递信息。JWT使用HMAC算法或者是RSA的公私秘钥的数字签名技术,所以这些信息是可被验证和信任的。

        JWT官网: https://jwt.io/

        JWT(Java版)的github地址: https://github.com/jwtk/jjwt

        在使用 JWT 前,需要先了解它的组成结构。它是由以下三段信息构成的:

  • Header 头部(包含签名和/或加密算法的类型)

  • Payload 载荷 (存放有效信息)

  • Signature 签名/签证

        将这三段信息文本用‘.’连接一起就构成完整的JWT字符串(比如:xxxxx.yyyyy.zzzzz),也是就我们需要的Token。

2. JWT的请求流程

        JWT的请求流程也特别简单,首先使用账号登录获取Token,然后后面的各种请求,都带上这个Token即可。具体流程如下:

  1. 客户端发起登录请求,传入账号密码;

  2. 服务端使用私钥创建一个Token;

  3. 服务器返回Token给客户端;

  4. 客户端向服务端发送请求,在请求头添加中该Token;

  5. 服务器验证该Token;

  6. 返回结果。

3. Session认证与JWT认证的区别 

        基于session和基于jwt的方式的主要区别就是用户的状态保存的位置。

        session是保存在服务端的,下一次再取从session当中取数据。

        jwt认证:用户输入用户名与密码,校验(从数据库当中查看有没有对应的数据),如果有对应的数据,会把用户取出来,把取出的用户数据,转成JWT,以token令牌的形式传给前端,前端拿到数据之后,会给存储到cookie,以后面每一次请求都要携带token,服务器就会获取token之后,再进行jw解析,读取用户数据,如果没有数据,就代表没有登录,而jwt是保存在客户端的。

4 JWT优缺点

4.1 优点

  •  jwt基于json,非常方便解析
  • 可以在令牌中自定义丰富的内容,易扩展
  • 通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高
  • 资源服务使用JWT可不依赖认证服务即可完成授权

4.2 缺点

  • JWT令牌较长,占存储空间比较大

5. 快速入门

5.1 创建工程

         

        设置Maven

        设置自动导入包 Auto Import

        设置启动注解 Annotation Processors

5.2 导入依赖

<!--引入springboot依赖-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>

<dependencies>
    <!--引入spring-boot启动器依赖, 添加启动器后web工程常用的依赖会自动帮你引入-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <!--Swagger2-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.7.0</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.7.0</version>
    </dependency>
    <!-- jwt -->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.4.0</version>
    </dependency>
</dependencies>

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

5.3 添加配置文件

        resources\application.yml 当中 添加配置信息

server:
  port: 8081

5.4 添加Swagger2配置类

package com.miaxis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.miaxis"))// 指定扫描包下面的注解
                .paths(PathSelectors.any())
                .build();
    }
    // 创建api的基本信息
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("集成Swagger2构建RESTful APIs")
                .description("集成Swagger2构建RESTful APIs")
                .termsOfServiceUrl("https://www.miaxis.com")
                .contact("Mickey")
                .version("1.0.0")
                .build();
    }
}

5.5 添加JWT工具类

package com.miaxis.util;

import com.miaxis.user.entity.User;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.logging.log4j.util.Strings;

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

/**
 * <p> @Title TokenUtil
 * <p> @Description Token 工具类
 */
public class TokenUtil {

    public static final String JWT_SECRET_KEY = "testjwt";

    /**
     * 创建Token
     *
     * @param user 用户实体
     * @return Token
     */
    public static String createToken(User user) {

        // 登录成功后生成JWT
        // JWT的header部分,该map可以是空的,因为有默认值{"alg":HS256,"typ":"JWT"}
        Map<String, Object> map = new HashMap<>();

        // 30分钟过期时间
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.MINUTE,30);

        return JWT.create()
                // 添加头部
                .withHeader(map)
                // 添加payload
                .withClaim("userId",user.getId())
                .withClaim("username",user.getUsername())
                .withClaim("email",user.getEmail())
                // 设置过期时间
                .withExpiresAt(instance.getTime())
                // 设置签名 密钥
                .sign(Algorithm.HMAC256(JWT_SECRET_KEY));
    }

    /**
     * 检查Token是否有效
     *
     * @param token Token
     * @return 是否有效
     */
    public static boolean isValid(String token) {
        if (Strings.isNotBlank(token)) {
            try {
                //创建验证对象,这里使用的加密算法和密钥必须与生成TOKEN时的相同否则无法验证
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(JWT_SECRET_KEY)).build();
                //验证JWT
                DecodedJWT decodedJwt = jwtVerifier.verify(token);
                return new Date().before(decodedJwt.getExpiresAt());
            } catch (Exception e) {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 检查Token所有Claims
     *
     * @param token Token
     * @return Claims
     */
    public static Map<String, Object> getClaims(String token) {

        if (isValid(token))  {
            //创建验证对象,这里使用的加密算法和密钥必须与生成TOKEN时的相同否则无法验证
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(JWT_SECRET_KEY)).build();
            //验证JWT
            DecodedJWT decodedJwt = jwtVerifier.verify(token);

            //获取JWT中的数据,注意数据类型一定要与添加进去的数据类型一致,否则取不到数据
            Map<String, Object> map = new HashMap<>();
            map.put("userId", decodedJwt.getClaim("userId").asInt());
            map.put("username", decodedJwt.getClaim("username").asString());
            map.put("email", decodedJwt.getClaim("email").asString());
            map.put("expire", decodedJwt.getExpiresAt());
            return map;
        } else {
            throw new RuntimeException("Token验证失败,请重新登录");
        }
    }
}

5.6 添加entity、service、controller类

  • entity\User
package com.miaxis.user.entity;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * <p> @Title User
 * <p> @Description 用户实体
 */
@Data
@AllArgsConstructor
public class User {
    /**
     * 主键
     */
    public Integer id;

    /**
     * 用户名
     */
    public String username;

    /**
     * 密码
     */
    public String password;

    /**
     * 邮箱
     */
    public String email;
}
  • service\UserService
package com.miaxis.user.service;

import com.miaxis.user.entity.User;
import org.springframework.stereotype.Service;

/**
 * <p> @Title UserService
 * <p> @Description 用户 业务层
 */
@Service
public class UserService {

    public User findUser() {
        return new User(1, "ACGkaka", "123456", "123@123.com");
    }
}
  • controller\UserController

   controller带token的接口配置Swagger带token

package com.miaxis.user.controller;

import com.miaxis.user.entity.User;
import com.miaxis.user.service.UserService;
import com.miaxis.util.TokenUtil;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * <p> @Title DemoController
 * <p> @Description 登录页面
 */
@RestController
@RequestMapping("index")
public class UserController {
    @Resource
    private UserService userService;

    /**
     * 登录
     *
     * @return Token
     */
    @ResponseBody
    @PostMapping("/login")
    public Map<String, Object> login() {
        System.out.println("============login");
        User user = userService.findUser();
        String token = TokenUtil.createToken(user);
        Map<String, Object> result = new HashMap<>();
        result.put("code", 200);
        result.put("message", "登录成功");
        result.put("data", token);
        return result;
    }

    /**
     * 获取Token信息
     *
     * @return 验证结果
     */
    @ResponseBody
    @GetMapping("/getInfo")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", name = "accessToken", required = true),
    })
    public Map<String, Object> getInfo(HttpServletRequest request) {
        Map<String, Object> result = new HashMap<>();
        String token = request.getHeader("accessToken");
        Map<String, Object> data = TokenUtil.getClaims(token);
        result.put("code", 200);
        result.put("message", "验证成功");
        result.put("data", data);
        return result;
    }
}

5.7 添加拦截器类

package com.miaxis.interceptor;

import com.miaxis.user.service.UserService;
import com.miaxis.util.TokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * <p> @Title TokenInterceptor
 * <p> @Description Token 验证拦截器
 */
public class TokenInterceptor implements HandlerInterceptor {

    @Autowired
    UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) {
        // 从 http 请求头中取出 token
        String token = httpServletRequest.getHeader("accessToken");
        // 如果不是映射到方法直接通过
        if(!(object instanceof HandlerMethod)){
            return true;
        }
        // 执行认证
        if (token == null) {
            throw new RuntimeException("accessToken不存在,请重新登录");
        }
        // 验证 token
        if (TokenUtil.isValid(token)) {
            return true;
        } else {
            throw new RuntimeException("Token验证失败,请重新登录");
        }
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

5.8 添加拦截器配置文件

  • 拦截器配置排除拦截Swagger

package com.miaxis.config;

import com.miaxis.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * <p> @Title InterceptorConfig
 * <p> @Description 拦截器配置
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //定义排除swagger访问的路径配置
        String[] swaggerExcludes=new String[]{"/swagger-ui.html","/swagger-resources/**","/webjars/**"};

        // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/index/login")
                .excludePathPatterns(swaggerExcludes);
        WebMvcConfigurer.super.addInterceptors(registry);
    }

    @Bean
    public TokenInterceptor authenticationInterceptor() {
        return new TokenInterceptor();
    }
}

5.9 创建启动类

package com.miaxis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

5.10 测试

5.10.1 通过swaggler测试  

        http://localhost:8081/swagger-ui.html

5.10.2 通过Postman测试

        http://localhost:8081/index/login

        http://localhost:8081/index/getInfo

        accessToken

        eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDcwMzc3NDUsInVzZXJJZCI6MSwiZW1haWwiOiIxMjNAMTIzLmNvbSIsInVzZXJuYW1lIjoiQUNHa2FrYSJ9.TT4CZgUfmWCNhbQa6UTWhSl3Tcf48uAVsAyqNQoR6KI

6. Demo下载地址

编译器版本:IntelliJ IDEA 2020.3.2 x64

JDK版本:java 1.8.0_111

下载地址:https://download.csdn.net/download/mickey2007/89110981

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/554726.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux:Redis7.2.4的源码包部署(2)

本章使用的是centos9进行部署 1.获取rpm安装包 Index of /releases/ (redis.io)https://download.redis.io/releases/这个网站有历史的版本&#xff0c;我这里使用的是最新版7.2.4进行安装 点击即可进行下载 方进Linux中&#xff0c;如果你的Linux中可以直接使用wget去下载 2…

Python创建并执行邮件合并,定制Word文档轻松批量创建

邮件合并是现代办公中一项显著提升效率的技术&#xff0c;它巧妙地将大量个体数据与预设的文档模板相结合&#xff0c;实现了一次性批量生成定制化文档。这一过程不仅极大地节省了手动重复录入的时间&#xff0c;更确保了信息传递的一致性和准确性&#xff0c;广泛应用于诸如批…

C++入门:运算符重载及日期类的实现

目录 1.赋值运算符重载 1.1 运算符重载 1.2赋值运算符重载 1.3引用作为返回参数☆☆ 1.4深入赋值运算符重载 2.实现日期Date类 2.1类之间的运算符重载 2.1.1相等 2.1.2小于 2.1.3复用实现其他 2.2类与整形之间的运算符重载 2.3单目操作符的重载 3. 流插入、流…

物联网在工业中的应用是什么?——青创智通

工业物联网解决方案-工业IOT-青创智通 物联网在工业中的应用已经日益广泛&#xff0c;它为企业带来了前所未有的机会和挑战。物联网技术通过连接各种设备和系统&#xff0c;实现了数据的实时采集、分析和优化&#xff0c;从而提高了生产效率、降低了成本并提升了企业的竞争力。…

如何实现视频扫码看的效果?视频二维码在线的制作技巧

现在分享视频很多人会采用生成二维码的方法&#xff0c;让其他人可以扫描二维码在手机上看视频&#xff0c;从而实现快速传播分享&#xff0c;有利于用户获取内容的便捷性。将视频存入云端服务器后&#xff0c;通过扫码调取视频内容&#xff0c;无需传统的方式将视频下载保存到…

CV | FSGS使用高斯喷溅的实时少样本视图合成论文详解与项目实现

本文是对论文的详解与项目实现。 Paper:2023.12.01_FSGS: Real-Time Few-Shot View Synthesis using Gaussian Splatting arxiv.org/pdf/2312.00451.pdf Code:VITA-Group/FSGS: "FSGS: Real-Time Few-Shot View Synthesis using Gaussian Splatting", Zehao Zhu, Zhi…

DMR数字对讲机模块的特性有哪些?该如何选择?

DMR828S是思为无线公司研发的一款性价比高的2W全功能数字对讲机模块&#xff0c;可以和市场上通用的模拟制式对讲机兼容&#xff0c;带有DMR TierII数字对讲机的功能&#xff0c;内置Moto AMBE 声码器。模块内部集成了微控制器、数字对讲芯片、射频功放以及音频功放等电路&…

QT打包发布

参考&#xff1a;QT项目打包成软件进行发布的三种方式_qt程序打包-CSDN博客 这里只研究前两种&#xff1a; ①小技巧加图标&#xff1a; &#xff08;1&#xff09;下载一个合适的图标文件 .ico 格式。 &#xff08;2&#xff09;将图标放在QT工程的根目录&#xff0c;然后在…

【尚硅谷】Git与GitLab的企业实战 学习笔记

目录 第1章 Git概述 1. 何为版本控制 2. 为什么需要版本控制 3. 版本控制工具 4. Git简史 5. Git工作机制 6. Git和代码托管中心 第2章 Git安装 第3章 Git常用命令 1. 设置用户签名 1.1 基本语法 1.2 案例实操 2. 初始化本地库 2.1 基本语法 2.2 案例实操 3. 查…

利用常量数组解码的方法

【题目描述】 把手放在键盘上时&#xff0c;稍不注意就会往右错一位。这样&#xff0c;输入Q会变成输入W&#xff0c;输入J会变成输入K等。键盘如图所示。 输入错位后敲出的几行字符串&#xff0c;输出打字员本来想打出的句子。 输入仅包含数字、空格、大写字母或标点符号&am…

2025中国国际储能大会暨展览会(简称“CIES”)

2025年第十五届中国国际储能大会暨展览会 2025 15th China International Energy StorageConference and Exhibition 时间&#xff1a;2025年3月23-26日 地点&#xff1a;杭州国际博览中心主办单位&#xff1a;中国化学与物理电源行业协会承办单位&#xff1a;中国化学与物理电…

Argo基础课程3-BGC-Argo数据质量控制

数据获取&#xff1a; Coriolis/France: https://data-argo.ifremer.fr/dac/ FNMOC/USA: https://usgodae.org/pub/outgoing/argo/dac/ Real-Time数据 24小时内发布&#xff0c;提交至自动质量控制平台进行标记和调整&#xff0c;“R” Delayed-mode数据 经过仔细浏览时间序列…

【Python初学指南】:从零开始学习Python烟花代码实战案例

1. Python入门基础 Python是一种高级编程语言&#xff0c;具有简单易学、功能强大的特点。通过安装Python环境&#xff0c;我们可以进行第一个Python程序的编写和运行。Python的入门基础对于初学者来说是非常重要的第一步&#xff0c;因为它奠定了后续学习和应用的基础。 在P…

哪种裤子穿起来舒服?夏季舒适透气的男装裤子分享

想必大家都会十分注重自己的外观形象&#xff0c;所以对选择的衣服上有不少要求。但是现在市面上却有很多质量不好的裤子&#xff0c;一些商家为了利润而使用一些舒适性差的面料。 那到底裤子哪个品牌的质量比较好&#xff1f;作为一名服装测评师&#xff0c;这些年测评过的品牌…

open Gauss 数据库-05 openGauss数据库备份恢复指导手册

发文章是为了证明自己真的掌握了一个知识&#xff0c;同时给他人带来帮助&#xff0c;如有问题&#xff0c;欢迎指正&#xff0c;祝大家万事胜意&#xff01; 目录 前言 openGauss数据库备份恢复 1 实验介绍 1.1 关于本实验 1.2 实验目的 2 实验前提 3 物理备份和恢复…

json diff patch

文件和图片的比对靠字符串 目录 流程 安装 效果 使用 自适应 数组&#xff1a;最长公共子序列(LCS) 数组中的对象&#xff0c;给定id&#xff0c;类似dom tree的比较 流程 安装 npm install jsondiffpatch import * as jsondiffpatch from jsondiffpatch; const jsond…

中电金信:夯实云原生时代的系统韧性建设——中电金信混沌工程金融业实践

IT系统建设在经历过单机、集中、分布式的演变历程后&#xff0c;系统运维演练、故障模拟测试的复杂度也不断提高。在复杂的分布式系统中&#xff0c;基础设施、应用平台都可能产生不可预知的故障&#xff0c;在不能确知故障根源的情况下&#xff0c;我们无法阻止故障的发生。更…

Transform结构

面试者经常会问transform这个模型&#xff0c;一个典型的seq2seq结构。 1 背景 试问几个问题&#xff0c;为什么提出了transform模型。RNN对于长时间序列&#xff08;超过40&#xff09;压缩到一个上下文向量中出现记忆退化现象&#xff0c;无法更好捕捉上下文信息。因此trans…

C语言知识(1) static修饰详解分享

1.前言 哈喽大家好啊&#xff0c;今天来给大家分享c中static的使用&#xff0c;希望能对大家有所帮助&#xff0c;请大家多多点赞&#xff0c;收藏支持我哦~ 2.正文 在讲解static之前&#xff0c;先给大家铺垫三个概念&#xff0c;方便大家理解。 2.1三则知识铺垫 2.1.1作…

写了一个 SRE 调试工具,类似一个小木马

远程操作机器有时会比较麻烦&#xff0c;我写了一个工具&#xff0c;主要功能&#xff1a;1.远程执行命令 2.上传下载文件。是一个 Web Server&#xff0c;通过 HTTP 请求来操作机器&#xff0c;类似一个小木马。当然&#xff0c;因为是一个 Web Server&#xff0c;所以也提供了…