SpringSecurity6.0+Redis+JWT基于token认证功能开发(可用于实际生产项目,保证API安全)

news2024/12/24 8:29:36

基于token认证功能开发

引子:最近做项目时遇到了一个特殊的需求,需要写共享接口把本系统的一些业务数据共享给各地市的自建系统,为了体现公司的专业性以及考虑到程序的扩展性(通过各地市的行政区划代码做限制),决定要把接口做的高级一些,而不是简单的传个用户名和密码对比数据库里面的,那样真的很low。于是写了基于token的认证功能,在这里分享出来供大家学习与探讨。

1、项目初始化

项目的初始化很重要,我们需要事先准备好一些通用的工具类和配置类,便于后面开发。

因为新建工程比较简单,这里就不啰嗦了,看下我添加了那些GAV坐标即可。

注意我用的SpringBoot版本是3.0的,如果版本和我保持一致的话pom.xml也需要保持一致否则依赖可能下载不下来(SpringBoot3.0当时还没有稳定版本的)。

1、pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent -->
    <!-- 所有SpringBoot项目都要继承spring-boot-starter-parent -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <groupId>com.laizhenghua</groupId>
    <artifactId>demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <url>https://repo.spring.io/snapshot</url>
            <!-- 表示只会去仓库查找稳定版本(releases=true)不会去查找开发中的版本(snapshots=false) -->
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

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

</project>

2、统一返回类R封装

为了避免API返回的数据混乱,我们统一使用R类进行返回,R中返回的数据结构如下

{
  "msg": "success", // 附加消息
  "code": 200, // 状态码(可以自定义不一定完全与http状态码一样)
  "data": "alex", // 数据统一放在data方便前端拦截器直接拦截data
  "success": true // 成功标识(是否成功可以通过这个属性判断)
}

新建utils.R.java(所有工具类都放在utils包下)

R.java

/**
 * TODO
 *
 * @Description 统一返回类封装
 * @Author laizhenghua
 * @Date 2023/2/19 20:04
 **/
public class R extends HashMap<String, Object> {

    private static final long serialVersionUID = 563554414843661955L;

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

    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    public static R success(Object data, String msg) {
        R r = new R();
        r.put("code", 200);
        r.put("data", data);
        r.put("msg", msg);
        r.put("success", true);
        return r;
    }

    public static R success(Object data) {
        return success(data, "success");
    }

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

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

    public static R ok() {
        return new R();
    }

    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}

2、RedisTemplate序列化配置

RedisTemplate默认采用JDK的序列化方式,一是不支持跨语言,最重要的是出了问题,排查起来非常不方便!因此为了保证序列化不出问题,我们需要重新配置RedisTemplate

新建config.RedisConfiguration.java(所有配置类都放在config包下)

RedisConfiguration.java

/**
 * TODO
 *
 * @Description RedisTemplate 序列化配置
 * @Author laizhenghua
 * @Date 2023/6/25 21:22
 **/
@Configuration
public class RedisConfiguration {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 我们为了开发方便直接使用<String,Object>泛型
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 序列化配置
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String序列化的配置
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化
        template.setKeySerializer(stringRedisSerializer);
        // Hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value采用Jackson2JsonRedisSerializer的序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash的value也采用jackson2JsonRedisSerializer的序列化方式
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

测试一下redis缓存有没有问题

/**
 * TODO
 *
 * @Description
 * @Author laizhenghua
 * @Date 2023/6/3 09:04
 **/
@RestController
public class HelloController {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/hello")
    public R hello() {
        redisTemplate.opsForValue().set("name", "alex");
        return R.success(redisTemplate.opsForValue().get("name"));
    }
}

浏览器访问这个API,惊奇的发现自动跳转到了登录页面需要认证后才能访问API

在这里插入图片描述

认证方式也很简单可以输入用户名和密码进行认证,如

Username: user
Password: 启动项目控制台输出的uuid
// 如 Using generated security password: f4895be9-132b-4627-a7e6-25b9b5baeb1b

// 用户名为什么user?源码如下
@ConfigurationProperties(prefix = "spring.security")
public class SecurityProperties {
	...
	public static class User {
	/**
	 * Default user name.
	 */
	// 当然也可以通过配置文件去指定
	private String name = "user";

	/**
	 * Password for the default user name.
	 */
	private String password = UUID.randomUUID().toString();
	...
	}
}

3、SpringSecurity初步配置

以上除了在登录页面输入用户名和密码进行认证外,还有一种方式就是在请求头或其他地方增加token,通过解析token找到认证用户并给予认证(本文就是介绍这种方式)。

当然也可以配置这个请求不需要认证也不需要鉴权,这也是测试例子想引出的知识点,因为后面静态资源和一些特殊的请求是不需要认证的比如说swagger相关的。

新建SecurityConfiguration.java配置类

SecurityConfiguration.java

/**
 * TODO
 *
 * @Description SecurityConfiguration
 * @Author laizhenghua
 * @Date 2023/6/25 21:55
 **/
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
    // 1.需要注意的是SpringSecurity6.0版本不再是是继承WebSecurityConfigurerAdapter来配置HttpSecurity,而是使用SecurityFilterChain来注入
    // 2.SpringSecurity6.0需要添加@EnableWebSecurity来开启一些必要的组件
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 关闭csrf因为不使用session
        http.csrf().disable()
                // 不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeHttpRequests()
                // 配置不需要认证的请求
                .requestMatchers("/hello").permitAll()
                // 除了上面那些请求都需要认证
                .anyRequest().authenticated();
        return http.build();
    }
    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
}

4、SpringBoot3.0集成Swagger3.0

END

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java框架之spring AOP 和 IOC

写在前面 本文一起看下spring aop 和 IOC相关的内容。 1&#xff1a;spring bean核心原理 1.1&#xff1a;spring bean的生命周期 spring bean生命周期&#xff0c;参考下图&#xff1a; 我们来一步步的看下。 1 其中1构造函数就是执行类的构造函数完成对象的创建&#x…

第八十四天学习记录:Linux基础:初识Linux

流行的Linux发行版&#xff1a; 任何人都可以封装Linux&#xff0c;目前市面上有非常多的Linux发行版&#xff0c;常用的知名的如下&#xff1a; VMware WorkStations安装 安装完成后&#xff0c;要通过下图方式查看网络适配器是否正常配置&#xff1a; 配置成功&#xff1a…

软件需求分析文档怎么写?

什么是软件需求规范文档 &#xff08;SRS&#xff09;&#xff1f; 软件需求规范 &#xff08;SRS&#xff09; 文档列出了未来项目的需求、期望、设计和标准。其中包括规定项目目标的高级业务需求、最终用户要求和需求以及产品在技术方面的功能。简而言之&#xff0c;SRS 提供…

Vue-Element-Admin项目学习笔记(8)配置表单校验规则

前情回顾&#xff1a; vue-element-admin项目学习笔记&#xff08;1&#xff09;安装、配置、启动项目 vue-element-admin项目学习笔记&#xff08;2&#xff09;main.js 文件分析 vue-element-admin项目学习笔记&#xff08;3&#xff09;路由分析一:静态路由 vue-element-adm…

软考:中级软件设计师:校验码,汉明码纠错,信息位L和校验位r的关系

软考&#xff1a;中级软件设计师:校验码&#xff0c;汉明码纠错 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是…

Linux通过crontab定时执行脚本任务

Linux通过crontab定时执行脚本任务 前言1. 创建写入脚本2. 设置执行权限3. 添加定时任务定时任务语法格式每分钟写入一条信息到指定文件 4. 查看日志文件5. 定时执行脚本的作用和用途 前言 在Linux中可以使用crontab来定时执行脚本。crontab是一个用于管理定时任务的工具&…

我这样回答多线程并发,面试官直接惊叹!

目录 前言&#xff1a; 1.单线程执行 2、多线程执行 3.守护线程 4.阻塞线程 前言&#xff1a; 多线程并发是一种处理任务的方式&#xff0c;它可以在同一时间内执行多个任务。多线程并发通常应用于需要同时处理多个任务或同时运行多个程序的情况下。 1.单线程执行 Pyth…

便携式水污染检测设备可以分析多少项污水指标

便携式水污染检测设备可以分析多少项污水指标&#xff08;以下只是一部分&#xff09; 水质检测仪可检测范围 1、饮用水检测&#xff1a;生活用水&#xff08;自来水&#xff09;、&#xff08;瓶、桶装&#xff09;矿泉水、天然矿泉水等&#xff1b; 2、工业用水检测&#xf…

人机融合智能的现状与展望

本篇文章是博主在人工智能等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在学习摘录和笔记专…

【开源库剖析】Shadow v2.3.0 源码解析

作者&#xff1a;Stan_Z 一、框架介绍 Shadow是19年腾讯开源的自研Android插件化框架&#xff0c;经过线上亿级用户量检验。 Shadow不仅开源分享了插件技术的关键代码&#xff0c;还完整的分享了上线部署所需要的所有设计。 优点&#xff1a; 1&#xff09;复用独立安装app源…

Python可视化库之Matplotlib详解及使用方法

Matplotlib是Python中最常用的可视化工具之一,可以非常方便地创建海量类型的2D图表和一些基本的3D图表。本文主要推荐一个学习使用Matplotlib的步骤。 基本前提 如果你除了本文之外没有任何基础,建议用以下几个步骤学习如何使用matplotlib: 学习基本的matplotlib术语,尤其是…

第二十二章Java一维数组的定义、赋值和初始化

当数组中每个元素都只带有一个下标时&#xff0c;这种数组就是“一维数组”。一维数组&#xff08;one-dimensional array&#xff09;实质上是一组相同类型数据的线性集合&#xff0c;是数组中最简单的一种数组。 数组是引用数据类型&#xff0c;引用数据类型在使用之前一定要…

Reactor的概念

一、Reactor的概念 ​ Reactor模式是一种事件驱动模式&#xff0c;由一个或多个并发输入源&#xff08;input)&#xff0c;一个消息分发处理器&#xff08;Initiation Dispatcher&#xff09;&#xff0c;以及每个消息对应的处理器&#xff08;Request Handler&#xff09;构成…

Linux安装nodejs

一、下载包 https://registry.npmmirror.com/binary.html?pathnode/ 比如&#xff1a;10.9.0 https://registry.npmmirror.com/binary.html?pathnode/v10.9.0/ 按需下载 https://registry.npmmirror.com/-/binary/node/v10.9.0/node-v10.9.0-linux-x64.tar.gz 二、上传到…

使用Nginx+Lua实现自定义WAF(Web application firewall)

转载https://github.com/unixhot/waf WAF 使用NginxLua实现自定义WAF&#xff08;Web application firewall&#xff09; 功能列表&#xff1a; 支持IP白名单和黑名单功能&#xff0c;直接将黑名单的IP访问拒绝。 支持URL白名单&#xff0c;将不需要过滤的URL进行定义。 支持…

解析vcruntime140.dll文件,缺失了要怎么去修复?

在计算机的世界中&#xff0c;vcruntime140.dll是一个重要的动态链接库文件。然而&#xff0c;有时候这个文件可能会引发一系列问题&#xff0c;影响应用程序的正常运行。如果你缺少了vcruntime140.dll&#xff0c;那么你的程序就会打不开&#xff0c;今天我们一起来聊聊vcrunt…

408数据结构第四章

串 定义存储结构模式匹配 小题形式考&#xff0c;比较简单&#xff0c;拿两个题来练手就会了 定义 字符串简称串 由零个或多个字符组成的有限序列 S是串名n称为串的长度&#xff0c;n0称为空串 串中多个连续的字符组成的子序列称为该串的子串 串的逻辑结构和线性表极为相似&am…

ByteHouse+Apache Airflow:高效简化数据管理流程

Apache Airflow 与 ByteHouse 相结合&#xff0c;为管理和执行数据流程提供了强大而高效的解决方案。本文突出了使用 Apache Airflow 与 ByteHouse 的主要优势和特点&#xff0c;展示如何简化数据工作流程并推动业务成功。 主要优势 可扩展可靠的数据流程&#xff1a;Apache Ai…

使用MASA Stack+.Net 从零开始搭建IoT平台 第五章 使用时序库存储上行数据

目录 前言分析实施步骤时序库的安装解决playload没有时间戳问题代码编写 总结 前言 我们可以将设备上行数据存储到关系型数据库中&#xff0c;我们需要两张带有时间戳的表&#xff08;最新数据表 和 历史数据表&#xff09;&#xff0c;历史数据表存储所有设备上报的数据&…

iptables详解

iptables简介 netfilter/iptables&#xff08;简称为iptables&#xff09;组成Linux平台下的包过滤防火墙&#xff0c;完成封包过滤、封包重定向和网络地址转换&#xff08;NAT&#xff09;等功能。 iptables 规则&#xff08;rules&#xff09;其实就是网络管理员预定义的条…