一文上手SpringSecuirty【六】

自定义认证流程完成之后,前端收到了后端生成的token,那么在之后的所有请求当前,都必须携带token.作为服务器来说,得验证这个token,是否合法.

一、验证token是否合法

1.1 OncePerRequestFilter过滤器

OncePerRequestFilter是 Spring 框架中的一个过滤器,用于确保在一次请求处理过程中只执行一次特定的过滤逻辑。其主要作用为:

  • 单次请求过滤:
  • 这个过滤器的设计目的是为了避免在一个请求的处理过程中重复执行相同的过滤操作。例如,如果有一些资源初始化、安全检查或日志记录等操作只需要在每个请求中执行一次,就可以使用OncePerRequestFilter来确保这些操作不会被重复执行。
  • 这样可以提高应用程序的性能和效率,避免不必要的重复计算和资源消耗。
    其次它是一个抽象类,OncePerRequestFilter是一个抽象类,我们可以继承这个类来实现自己的过滤器逻辑,在子类中,可以重写doFilterInternal方法来定义具体的过滤操作。通过继承OncePerRequestFilter,可以利用其已经实现的一些基础功能,如确保单次执行和处理请求的流程控制。

可以将其应用在如下的场景当中:

  • 安全检查:
    可以使用OncePerRequestFilter来执行一些安全检查操作,如身份验证、授权检查或防止跨站请求伪造(CSRF)攻击。这些操作通常只需要在每个请求中执行一次,以确保请求的安全性。
  • 资源初始化:
    如果在请求处理过程中需要初始化一些资源,如数据库连接、缓存或其他共享资源,可以使用OncePerRequestFilter来确保这些资源只在每个请求的开始阶段进行初始化,避免重复初始化带来的性能开销。
  • 对于一些需要记录请求信息的场景,可以使用OncePerRequestFilter来进行日志记录。这样可以确保在每个请求中只记录一次关键信息,避免日志过于冗长和重复。

1

1.2 实现token校验逻辑

@Component
@Slf4j
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 1. 从请求头当中取出前端传递过来的token
        String token = request.getHeader("token");
        // 2. 判断一下token是否为空
        if(!StringUtils.hasText(token)){
            // 如果请求头当中没有传递token,直接放行.交给下一下过滤器去处理即可.
            filterChain.doFilter(request, response);
            return;
        }

        // 3. 解析token
        try {
            Claims claims = JwtUtil.parseJWT(token);
            String id = claims.getId();
            String subject = claims.getSubject();
            log.info("id:{},subject:{}", id, subject);
            // 解析出来subject和id了,这里的subject就是用户名称,由于这个token是由服务器下发的,服务能给发token,表示
            // 肯定已经认证成功了.我们要做的是根据解析出来的token信息,去数据库查询,能不能找到匹配的信息.如果能,则表示
            // 这个用户已经认证过了,直接放行就行了,如果找不到,那这个token可能是伪造的,就不能让访问资源 如果不匹配,也
            // 不能访问资源

            // 这里我们并没有使用数据库作为数据源,默认的用户名称和密码都是admin,不过密码是加密处理的密文而已.
            // 根据用户名称查询用户信息. 【我们这里模拟一下这个操作】
            String name = "admin";
            String password = "$2a$10$4/S6K/z/nF5eTk9KlF/PgOGtv2jlLGrzpO3oXINQAkNNlMqtVT6ru";

            // 封装认证对象
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
                     = new UsernamePasswordAuthenticationToken(name, password, Collections.emptyList());

            // 存储用户认证凭证,已经校验通过,表示已经认证过了,那么spring security的后续过滤器链,必须得拿到这个结果
            // 否则的话,会被当作没有认证的用户,继续去执行认证流程的.
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            // 放行操作
            filterChain.doFilter(request, response);
        }catch (Exception e){ // token抛出异常了,譬如说,过期了, 伪造啥的.不管是啥,我们都直接返回就行了
            // 记录一下日志,直接返回即可
            // 将错误信息写回给浏览器
            ResponseWriteUtils.write2Client(response, Result.error(-1, "认证异常,请重新登录"));
        }
    }
}

一些细节问题:

  • 注意看代码里的注释信息,非常重要
  • UsernamePasswordAuthenticationToken 对象的构建必须用三个参数的构造方法,最后一个参数表示用户权限列表
    • 三个参数的构造方法被调用的时候,会将认证状态设置为已经认证, 源码如下所示
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
		Collection<? extends GrantedAuthority> authorities) {
	super(authorities);
	this.principal = principal;
	this.credentials = credentials;
	// 就是这句,设置是否已经被认证了
	super.setAuthenticated(true); // must use super, as we override
}
  • setAuthentication存储认证凭证这里,由于我们服务器下发了token,表示已经认证过了,凭证起来给再接下来的spring security过滤器链当中要被使用到,简单来说,后续的spring security过滤器链,一看你有认证凭证了,那么不会再做认证处理了.
  • ResponseWriteUtils.write2Client方法就是写一些字符串返回给浏览器
public class ResponseWriteUtils {

  /**
   * 向客户端写JSON串
   * @param response
   * @param result
   */
  public static void write2Client(HttpServletResponse response, Result result){
      response.setContentType("application/json;charset=UTF-8");
      response.setCharacterEncoding("UTF-8");

      try(PrintWriter writer = response.getWriter()){
          writer.println(JSON.toJSONString(result));
          writer.flush();
      }catch (Exception e){
          throw new RuntimeException(e);
      }
  }
}

1.3 将TokenAuthenticationFilter添加到spring security过滤器链当中

OncePerRequestFilter过滤器是由spring的提供的,本质上跟spring security没有半毛钱关系,但是我们代码当中:

String token = request.getHeader("token");
// 2. 判断一下token是否为空
if(!StringUtils.hasText(token)){
    // 如果请求头当中没有传递token,直接放行.交给下一下过滤器去处理即可.
    // 重点: 这里如果我们token没有,实际上我们是期望进入到spring security的认证流程的, 但是目前来说,自定义的过滤器和
    // spring security的过滤器链并没有啥关系
    filterChain.doFilter(request, response);
    return;
}

在spring security配置类当中,将我们自己定义的过滤器,添加到spring security的过滤器链当中,但是要注意添加位置,将其添加到UsernamePasswordAuthenticationFilter过滤器之前即可,HttpSecurity提供了相应的方法,如下所示:

@Configuration
public class SpringSecurityConfig {
    private final TokenAuthenticationFilter tokenAuthenticationFilter;

    public SpringSecurityConfig(TokenAuthenticationFilter tokenAuthenticationFilter) {
        this.tokenAuthenticationFilter = tokenAuthenticationFilter;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests(authorize -> {
            try {
                authorize.requestMatchers("/api/pub/v1/login").permitAll()
                        .requestMatchers("/static/**", "/resources/**").permitAll()
                        .anyRequest().authenticated();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).csrf(AbstractHttpConfigurer::disable)
                // 将我们自己定义的过滤器添加到 UsernamePasswordAuthenticationFilter过滤器之前
                .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}

1.4 编写一个测试接口

测试使用如下代码,获致菜单列表

@RestController
public class HomeController {

    @GetMapping("/api/pub/v1/menus")
    public Result menuList(){
        List<String> menuList = new ArrayList<>();
        menuList.add("商品管理");
        menuList.add("用户管理");
        return Result.success(0, "获取列表碾", menuList);
    }
}

前端添加请求接口的处理

export const getMenuList = () => $http({url: '/menus', method: 'get'})

在Home路由组件当中,使用它进行一个简单的测试

<template>
    <div>
        <h1>主页展示</h1>
    </div>
</template>

<script lang="ts">
export default {
name: 'Home'
}
</script>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import {getMenuList} from '../../api/home'
let menuList = ref([])

onMounted(async () => {
    const ret = await getMenuList()
    console.log(ret)
})

</script>
<style scoped>

</style>

1.5 测试

启动服务,点击登录,查看后台输出
token
33
111
data
log

二、总结

2.1 重点内容

  • 校验token的流程
  • 在自定义的过滤器当中,要注意这里其实有一个查询数据库操作,从token里获取用户名称,此时我们要去根据用户名称,去数据库当中查询出用户信息,才能拿到密码还有权限信息,我们在示例当中,只是模拟了一下操作而已

2.2 下篇内容

  • 替换为真实的数据源

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

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

相关文章

基于nodejs+vue的校园二手物品交易系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)

文章目录 esp-rs 简介GithubRust 包仓库Wokwi 电子模拟器开发环境Rust 环境esp-rs 环境创建 ESP32C3 项目项目结构编译项目命令运行模拟器ESP32C3 烧录 esp-rs 简介 esp-rs 是一个专注于为 Espressif 系列芯片&#xff08;如 ESP32、ESP32-S2、ESP32-C3 等&#xff09;提供 Ru…

如何在 Three.js 场景中创建可点击展开的标签

在复杂的可视化场景中&#xff0c;经常需要为 3D 对象添加可交互的标签&#xff0c;以便用户点击时可以查看详细信息。这篇文章将通过一个简单的案例展示&#xff0c;如何在 Three.js 中为对象创建可点击的标签&#xff0c;点击标签可以展开详细信息&#xff0c;再次点击可以关…

论文复现:考虑电网交互的风电、光伏与电池互补调度运行(MATLAB-Yalmip-Cplex全代码)

论文复现:考虑电网交互的风电、光伏与电池储能互补调度运行(MATLAB-Yalmip-Cplex全代码) 针对风电、光伏与电化学储能电站互补运行的问题,已有大量通过启发式算法寻优的案例,但工程上更注重实用性和普适性。Yalmip工具箱则是一种基于MATLAB平台的优化软件工具箱,被广泛应用…

基于单片机语音智能导盲仪仿真设计

文章目录 前言资料获取设计介绍设计程序具体实现截图设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们…

【Python】探索 Graphene:Python 中的 GraphQL 框架

人们常说挣多挣少都要开心&#xff0c;这话我相信&#xff0c;但是请问挣少了怎么开心&#xff1f; 随着现代 Web 应用对数据交互需求的不断增长&#xff0c;GraphQL 作为一种数据查询和操作语言&#xff0c;越来越受到开发者的青睐。Graphene 是 Python 语言中实现 GraphQL 的…

基于SpringBoot+Vue3的在线报名系统

一、项目介绍 1.1 项目介绍 本项目为一个报名系统&#xff0c;实现了基本的报名流程&#xff0c;功能完善&#xff0c;前后端皆有个人独立开发&#xff0c;功能相对不是特别难&#xff0c;但该有的功能还是都已经实现。 1.2 技术架构 主要技术栈&#xff1a; SpringBoot2 …

WebRTC中的维纳滤波器实现详解:基于决策导向的SNR估计

目录 1. 维纳滤波器的基本原理2. WebRTC中的维纳滤波器实现3. 代码逐步剖析4. 总结 在WebRTC的噪声抑制模块中&#xff0c;维纳滤波器&#xff08;Wiener Filter&#xff09;是一种非常常见且重要的滤波器&#xff0c;用于提高语音信号的清晰度并抑制背景噪声。本文将详细解释维…

erlang学习:Linux命令学习6

for循环学习 打印九九乘法表 for i in {1..9};do %%取1-9for j in $(seq 1 $i);do %%取1-iecho -n "$j*$i$((i*j)) " %%进行九九乘法表打印doneecho done尝试了很多次报错是因为后面的换行符不对&#xff0c;window系统中的换行符与linux对不上&#xff0c;因…

AI芯片WT2605C赋能厨房家电,在线对话操控,引领智能烹饪新体验:尽享高效便捷生活

在智能家居的蓬勃发展中&#xff0c;智能厨电作为连接科技与生活的桥梁&#xff0c;正逐步渗透到每一个现代家庭的厨房中。蒸烤箱作为智能厨电的代表&#xff0c;以其丰富的功能和高效的性能&#xff0c;满足了人们对美食的多样化追求。然而&#xff0c;面对众多复杂的操作功能…

OpenHarmony(鸿蒙南向)——平台驱动开发【MIPI DSI】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 概述 功能简介 DSI&#xff08;Display Serial Interface&#x…

小阿轩yx-案例:代码管理系统简介与部署

小阿轩yx-案例&#xff1a;代码管理系统简介与部署 前言 开发一个项目时&#xff0c;如果只有几十行代码或几百行代码时维护还算简单&#xff0c;但是代码数量达到一定程度或两三个人共同开发一个项目时&#xff0c;就很容易会出现代码混乱、冲突、排错难等问题。代码编写完成…

【软件测试】如何设计测试用例? 设计测试用例常用的方法.

目录 一.什么是测试用例?二.总体设计测试用例的万能公式.2.1 功能性能界面兼容易用安全2.2 弱网测试2.3 安装卸载测试. 三. 常用设计具体测试用例的方法3.1 等价类3.2 边界值3.3 正交法3.3.1 正交表3.3.2 如何设计正交表,并根据正交表编写测试用例 3.4 判定表法3.4.1 根据判定…

828华为云征文 | 解锁高效项目管理,Zentao在华为云Flexusx容器化部署与应用指南

前言 在当今快速迭代的商业环境中&#xff0c;高效且灵活的项目管理成为企业竞争力的关键。华为云Flexusx实例&#xff0c;以其灵活的vCPU内存配比、热变配功能及按需计费模式&#xff0c;为项目管理软件如Zentao的部署提供了理想平台。Flexusx实例采用按需计费的灵活定价模式&…

Ansible流程控制-条件_循环_错误处理_包含导入_块异常处理

文章目录 Ansible流程控制介绍1. 条件判断2. 循环3. 循环控制4. 错误处理5. 包含和导入6. 块和异常处理7. 角色的流程控制*include_tasks、import_tasks_include之间的区别 条件语句再细说且、或、非、是模糊条件when指令的详细使用方法 循环语句再细说如何使用使用item变量结合…

甄选范文“论软件需求管理”,软考高级论文,系统架构设计师论文

论文真题 软件需求管理是一个对系统需求变更了解和控制的过程。需求管理过程与需求开发过程相互关联,初始需求导出的同时就要形成需求管理规划,一旦启动了软件开发过程,需求管理活动就紧密相伴。 需求管理过程中主要包含变更控制、版本控制、需求跟踪和需求状态跟踪等4项活…

???Ansible-使用roles

文章目录 一、Ansible的内置的或官方推荐创建的目录及文件介绍roles目录解释1、roles/自定义角色名目录下2、roles/自定义角色名目录/tasks目录下3、roles/自定义角色名目录/handlers目录下4、roles/自定义角色名目录/templates目录下5、roles/自定义项目名目录/files目录下6、…

SSM超市售卖管理系统-计算机毕业设计源码23976

目 录 摘要 Abstract 1 绪论 1.1研究的背景和意义 1.2研究内容 1.3论文结构与章节安排 2 开发技术介绍 2.1 SSM框架 2.2 MySQL数据库 3 超市售卖管理系统系统分析 3.1 可行性分析 3.2 系统流程分析 3.2.1 数据流程 3.3.2 业务流程 3.3 系统功能分析 3.3.1 功…

港科夜闻 | 香港科大颁授荣誉大学院士予五位杰出人士

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大颁授荣誉大学院士予五位杰出人士。香港科大9月24日向五位杰出人士颁授荣誉大学院士&#xff0c;他们分别为包弼德教授、简吴秋玉女士、高秉强教授、吴永顺先生及容永祺博士(按姓氏英文字母排序)。荣誉大学院士颁…

BUG——IMX6ULL编译正点原子Linux内核报错

最初编译的是正点原子改过的Linux内核&#xff0c;可能是版本问题&#xff0c;一直报错&#xff0c;无法成功编译。然后换成NXP官方Linux内核6.6版本&#xff0c;初始编译虽然也报各种错&#xff0c;但都是缺少库或相关工具&#xff0c;全部安装后就可以成功编译出镜像了&#…