0%

Spring Security 中获取用户信息的原理与实践

在 Web 应用中,Spring Security 能够在每次请求时识别用户身份并验证权限,核心在于 SecurityContextPersistenceFilterSecurityContext 的管理。本文结合源码解析这一机制的实现原理,并介绍在实际开发中获取用户信息的常用方式。

SecurityContext 的生命周期管理

SecurityContext 是存储用户认证信息的容器,其生命周期由 SecurityContextPersistenceFilter 全程管理,确保每个请求都能获取到当前用户的身份信息。

1. 核心流程:请求到来时加载上下文

当一个请求进入应用时,SecurityContextPersistenceFilter 会执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;

// 确保过滤器只对同一请求请求处理一次
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

HttpRequest// 1. 从 Session 中加载 SecurityContext
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

try {
// 2. 将上下文存入 SecurityContextHolder(ThreadLocal 存储)
SecurityContextHolder.setContext(contextBeforeChainExecution);
// 3. 执行后续过滤器链(包括权限校验等)
chain.doFilter(holder.getRequest(), holder.getResponse());
} finally {
// 4. 请求结束:清除 ThreadLocal 中的上下文
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
SecurityContextHolder.clearContext();
// 5. 将上下文保存回 Session
repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
}
}
阅读全文 »

MySQL 存储表情符号:utf8 与 utf8mb4 的区别及解决方案

MySQL 中存储表情符号(如 😄、🎉 等)时出现的 Incorrect string value 错误,根源是字符集对 Unicode 编码的支持不全。默认的 utf8 字符集无法存储 4 字节的表情符号,需改用 utf8mb4 字符集。本文详细解析两者的区别及完整解决方案。

错误原因:utf8 字符集的局限性

MySQL 中的 utf8 字符集存在设计缺陷:

  • 实际是 utf8mb3 的别名,仅支持最多 3 字节 的 Unicode 字符。
  • 而表情符号(如 😄 的 Unicode 编码为 U+1F604)属于 4 字节字符,超出了 utf8mb3 的存储范围,因此插入时会报错:
    1366 - Incorrect string value: '\xF0\x9F\x98\x84' for column 'name' at row 1

utf8 与 utf8mb4 的核心区别

字符集 最大字节数 支持的 Unicode 范围 能否存储表情符号 适用场景
utf8 3 字节 基本多文种平面(BMP,U+0000~U+FFFF) 不能 无表情、无特殊符号的场景
utf8mb4 4 字节 全部 Unicode 字符(含 U+10000~U+10FFFF) 需存储表情、Emoji、特殊符号的场景
  • 关键结论utf8mb4 是 MySQL 中真正完整支持 UTF-8 编码的字符集,utf8 是不完整的子集(仅 3 字节)。

存储表情符号的完整解决方案

要在 MySQL 中正确存储表情符号,需从 字段、表、数据库 三个层级修改字符集为 utf8mb4,并确保连接层也使用该字符集。

阅读全文 »

Elasticsearch 分页查询:从基础到深度分页的解决方案

在 Elasticsearch 中,分页查询是获取大量数据的常用操作,但不同分页方式的性能和适用场景差异显著。本文详细解析 from+sizescrollsearch_after 三种分页方式的原理、用法及优缺点,帮助你根据业务场景选择最优方案。

基础分页:from+size(浅分页)

from+size 是最直观的分页方式,通过 from(跳过的条数)和 size(返回的条数)控制分页,适合数据量小、分页深度浅的场景(如前 10 页)。

用法示例

1
2
3
4
5
6
7
# 基础语法
GET index/_search
{
"from": 100, # 跳过前100
"size": 20, # 返回20
"query": { "match_all": {} }
}

原理

  • 当请求 from=100,size=20 时,Elasticsearch 会向每个分片发送请求,要求每个分片返回 from+size=120 条数据。
  • 协调节点收集所有分片的 120 条数据后,进行全局排序,再截取第 101~120 条作为结果返回。

优缺点

  • 优点:实现简单,支持随机跳页(如直接从第 1 页跳到第 10 页)。
  • 缺点:
    • 深度分页(from 过大,如 from=10000)时,每个分片需返回大量数据(10000+size),导致网络传输和排序开销激增,甚至触发内存限制(默认 index.max_result_window=10000,超过会报错)。

游标分页:scroll(批量处理)

scroll 适用于批量处理全量数据(如数据导出、索引迁移),通过创建数据快照生成 scroll_id,后续基于游标获取下一页,不支持随机跳页。

用法示例

(1)初始化 scroll 并获取第一页
阅读全文 »

Tomcat 中的 Pipeline 与 Valve:请求处理的责任链模式

Tomcat 的 Pipeline(管道)和 Valve(阀门)是其核心组件,用于处理请求和响应的流程控制。它们基于责任链模式设计,将请求处理的不同任务拆解为独立的阀门,通过管道串联执行,实现了请求处理逻辑的解耦与灵活扩展。本文将深入解析 Pipeline 与 Valve 的工作原理、结构及配置方式。

Pipeline 与 Valve 的核心概念

基本定义

  • Pipeline(管道)
    每个 Tomcat 容器(EngineHostContextWrapper)都包含一个 Pipeline,它是一个任务执行链,负责按顺序调用一系列 Valve 处理请求。
  • Valve(阀门)
    每个 Valve 代表一个具体的处理任务(如日志记录、权限校验、请求转发等),类似于过滤器(Filter),但作用于 Tomcat 容器级别而非 Web 应用级别。
  • Basic Valve(基础阀门)
    每个 Pipeline 必须包含一个 Basic Valve,作为管道的最终处理器(责任链的终点),负责执行容器的核心逻辑(如 Wrapper 的 Servlet 调用、Host 的虚拟主机路由)。

设计思想:责任链模式

Pipeline 与 Valve 的设计借鉴了责任链模式,其核心特点是:

  • 每个 Valve 只处理自己负责的任务,处理完成后将请求传递给下一个 Valve。
  • 可通过添加 / 移除 Valve 灵活扩展请求处理逻辑,无需修改原有代码。
  • 不同容器的 Pipeline 独立工作,形成层级化的处理流程(如 Engine → Host → Context → Wrapper)。

Pipeline 与 Valve 的结构与实现

容器与 Pipeline 的对应关系

Tomcat 的四大容器(从顶层到应用层)均内置 Pipeline:

阅读全文 »

Maven 编码格式配置:统一项目编码避免乱码问题

在 Maven 构建过程中,若出现编码格式不一致(如默认 GBK 与项目 UTF-8 冲突),可能导致资源文件乱码、日志警告或构建失败。本文详细介绍如何统一 Maven 编码格式,确保项目构建的一致性。

编码问题的表现与影响

当 Maven 编码与项目编码不一致时,常见现象包括:

  • 构建日志出现警告:[WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform dependent!
  • 资源文件(如 *.properties*.xml)在过滤或复制过程中出现乱码。
  • 编译后的 class 文件包含中文乱码,导致运行时异常。

这些问题的核心原因是:Maven 默认使用操作系统的平台编码(如 Windows 通常为 GBK),而项目可能采用 UTF-8 编码。

全局编码配置(推荐)

通过配置环境变量,统一 Maven 所有项目的编码格式,一劳永逸解决问题。

配置 MAVEN_OPTS 环境变量

步骤

  1. 打开系统环境变量配置(Windows 可通过「此电脑→属性→高级系统设置→环境变量」)。

  2. 新建系统变量MAVEN_OPTS,值为:

    1
    -Dfile.encoding=UTF-8
  3. 重启命令行工具(确保环境变量生效)。

验证:执行 mvn --version,查看输出中 Default locale 对应的编码是否为 UTF-8:

1
Default locale: zh_CN, platform encoding: UTF-8
阅读全文 »