惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - Zhang_Xiang

代码是 AI 写的,生产事故谁背锅? AI Agent 走出 Demo 幻觉的唯一解药:Harness Engineering 从 page、page_size 到游标:深入解析C端产品的两种主流分页技术 Apache Kafka 的基本概念 Apache Kafka 移除 ZK Proposals webRTC demo Spring Authorization Server(AS)从 Mysql 中读取客户端、用户 Java 对象实现 Serializable 的原因 Spring Data JPA 使用 Spring Authorization Server 实现授权中心 OAuth 2.1 框架 dapr 本地环境升级 BuildPack 打包 spring-boot 2.5.4,nacos 作为配置、服务发现中心,Cloud Native Buildpacks 打包镜像,GitLab CI/CD 如何拆分大型单体系统为微服务 高可用 Keycloak,K8s Keycloak 13 自定义用户身份认证流程(User Storage SPI) - Zhang_Xiang OAuth 2.0、OIDC 讲不清楚? Mokito 单元测试与 Spring-Boot 集成测试 关于 JMeter 5.4.1 的一点记录
Spring Security
Zhang_Xiang · 2022-05-01 · via 博客园 - Zhang_Xiang

Spring Security


Spring Security 是一个提供认证、授权以及一些常见漏洞防护的框架。该框架为 Servlet 应用程序(Spring MVC)和响应式应用程序(Spring WebFlux,本文不表)提供防护,并作为防护 spring 项目的事实标准。

Servlet 应用程序

Spring Security 通过标准的 Servlet 过滤器 集成 Servlet 容器。这表明只要在 Servlet 容器中运行的应用程序都可以使用该框架。具体来说,你不需要在以 Servlet 为容器的应用程序中专门引入 Spring,就可以使用该框架。

在 Spring Boot 项目中使用 Spring Security

新建项目

build.gradle 中输入以下内容:

plugins {  
    id 'org.springframework.boot' version '2.6.7'  
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'  
    id 'java'  
}  
  
group = 'com.github.toy'  
version = '0.0.1-SNAPSHOT'  
sourceCompatibility = '17'  
  
repositories {  
    mavenCentral()  
}  
  
dependencies {  
    implementation 'org.springframework.boot:spring-boot-starter-security'  
    implementation 'org.springframework.boot:spring-boot-starter-web'  
    testImplementation 'org.springframework.boot:spring-boot-starter-test'  
    testImplementation 'org.springframework.security:spring-security-test'  
}  
  
tasks.named('test') {  
    useJUnitPlatform()  
}

运行

控制台输出

...
2022-04-30 16:21:28.626  WARN 37347 --- [           main] .s.s.UserDetailsServiceAutoConfiguration : 

Using generated security password: fceb50df-05cf-40e4-a4c5-67d5da55a6b4
...

Spring Boot Auto Configuration

Spring Boot 自动处理以下内容:

  • 启用 Spring Security 的默认配置,它会创建一个 servlet 过滤器 bean,叫做 springSecurityFilterChain。这个 bean 负责应用程序中的所有安全工作(保护应用程序的 URL,验证提交的用户名和密码,重定向登录表单,等等)。
  • 用户名(username)为 user,密码为 console 中生成的随机密码,使用此账号和密码创建 UserDetailsService bean。
  • 通过 Servlet 容器为每个请求注册名为 springSecurityFilterChain过滤器

Spring Boot 配置的内容不多,但是做的很多。功能总结如下:

Spring Security 在 Servlet 应用程序中的顶层架构

我们将基于认证、授权、Protection Against Exploits 来理解这种顶层架构

过滤器

Spring Security 的 Servlet 建立在 Servlet 的 过滤器 上,所以先查看 过滤器 的作用很有帮助。以下图片显示了单个请求的处理层级。

客户端给应用程序发送请求,容器创建一个包含过滤器ServletFilterChain ,它将处理基于请求 URI 路径的 HttpServletRequest。在 Spring MVC 应用程序中,ServletDispatcherServlet 的实例。一个 Servlet 最多只能处理一个 HttpServletRequestHttpServletReponse。不过,可以使用多个 过滤器 处理以下内容:

  • 阻止下游 过滤器 或者 Servlet 被调用。在这个实例中,过滤器 将写 HttpServletReponse
  • 修改下游 过滤器ServletHttpServletRequestHttpServletReponse

FilterChain 的能力来自于传入其中的 过滤器

FilterChain 用例

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
	// 在应用程序处理前,做一些逻辑处理
    chain.doFilter(request, response); // 调起应用程序处理
    // 在应用程序处理后,做一些逻辑处理
}

因为 过滤器 只影响下游的 过滤器Servlet,所以每个 过滤器 的调用顺序及其重要。

DelegatingFilterProxy

Spring 提供一个叫做 DelegatingFilterProxy 的实现,它桥接了 Servlet 容器的生命周期和 Spring 的 ApplicationContext。Servlet 容器允许其使用自己的标准注册 过滤器,但是它感应不到 Spring 定义的 bean。DelegatingFilterProxy 可以凭借 Servlet 容器的机制完成注册,但是所有的工作都委托给实现了 过滤器 的 Spring Bean。

以下是 DelegatingFilterProxy 如何融合 过滤器FilterChain 的图。

DelegatingFilterProxyApplicationContext 中查找并调用 Filter0 Bean。DelegatingFilterProxy 的伪代码如下。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
	// 加载注册为 Spring Bean 的过滤器
	// 例如在 DelegatingFilterProxy 中,delegate 是 Filter0 Bean 的实例
	Filter delegate = getFilterBean(someBeanName);
	// 将工作委托给 Spring Bean
	delegate.doFilter(request, response);
}

DelegatingFilterProxy 的另一个好处是可以延迟查找 过滤器 的实例。这种方式十分重要,原因是容器需要在启动之前注册 过滤器。不过,Spring 一般通过 ContextLoaderListener 加载 Spring 的 Bean,直到所有的 过滤器 注册完才结束。

FilterChainProxy

对 Spring Security 的 Servlet 支持,包含在 FilterChainProxy 中。FilterChainProxy 是 Spring Security 提供的一种特殊 过滤器,它可以通过 SecurityFilterChain 委托很多 过滤器。因为 FilterChainProxy 是一个 Bean,所以它一般封装在 DelegatingFilterProxy 中。

SecurityFilterChain

SecurityFilterChainFilterChainProxy 用于决定在该请求种哪个 Spring Security 的过滤器应该被调用。

Security Filters 在 SecurityFilterChain 一般以 Bean 的方式存在,它们通过 FilterChainProxy 注册,从而替代 DelegatingFilterProxyFilterChainProxy 直接通过 Servlet 容器或 DelegatingFilterProxy 注册,提供了诸多好处。首先,它为 Spring Security 的 Servlet 提供支撑起点。因此,如果你要解决 Spring Security Servlet 的问题,在 FilterChainProxy 中添加 debug 断点是不错的开始。

第二,由于 FilterChainProxy 是使用 Spring Security 的重点,它可以执行一些必要的任务。例如,清除 SecurityContext,从而避免内存泄漏。还可以应用于 Spring Security 的 HttpFirewall,从而保护应用程序免受某些类型的攻击。

此外,在决定何时调用 SecurityFilterChain 时,它提供了更多的灵活性。在 Servlet 容器中,只基于 URL 调用 过滤器。不过,FilterChainProxy 通过利用 RequestMatcher 接口,可以根据 HttpServletRequest 中的任何事情决定调用。

事实上,FilterChainProxy 可以用于决定应该调用哪个 SecurityFilterChain 。因此可以为应用程序的不同切片提供一个总体的分隔配置。

在上图中,FilterChainProxy 决定应该使用哪个 SecurityFilterChain。第一个匹配的 SecurityFilterChain 会被调用。如果发起一个 URL 为 /api/message 的请求,它将匹配 SecurityFilterChain 0 的模式 /api/** ,所以只会调用 SecurityFilterChain 0 ,即使也匹配 SecurityFilterChain n 。如果发起一个 URL 为 /messages 的请求,它不匹配 SecurityFilterChain 0 的模式 /api/**,所以 FilterChainProxy 将继续尝试每一个 SecurityFilterChain。如果没有匹配到,SecurityFilterChain 匹配的 SecurityFilterChain n 实例将会被调用。

注意,SecurityFilterChain 0 只配置了 3 个安全 过滤器。但是,SecurityFilterChain n 配置了 4 个 过滤器 实例。每个 SecurityFilterChain 可以孤立的配置,留意这一点很重要。事实上,SecurityFilterChain 可能不配置 过滤器,如果 Spring Security 要忽略某些请求。

Security Filters

Security Filters 通过 SecurityFilterChain API 插入到 [[#FilterChainProxy]] 中。[[#过滤器]] 的顺序十分重要。通常,无需了解 Spring Security 过滤器 的顺序。但是,有些时候了解它们排序是有益的。

以下是 Spring Security 过滤器的综合排序:

  • ChannelProcessingFilter
  • SecurityContextPersistenceFilter
  • WebAsyncManagerIntegrationFilter
  • HeaderWriterFilter
  • CorsFilter
  • CsrfFilter
  • LogoutFilter
  • OAuth2AuthorizationRequestRedirectFilter
  • Saml2WebSsoAuthenticationRequestFilter
  • X509AuthenticationFilter
  • AbstractPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter
  • OAuth2LoginAuthenticationFilter
  • Saml2WebSsoAuthenticationFilter
  • UsernamePasswordAuthenticationFilter
  • OpenIDAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • ConcurrentSessionFilter
  • DigestAuthenticationFilter
  • BearerTokenAuthenticationFilter
  • BasicAuthenticationFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • JaasApiIntegrationFilter
  • RememberMeAuthenticationFilter
  • AnonymousAuthenticationFilter
  • OAuth2AuthorizationCodeGrantFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor
  • SwitchUserFilter

处理 Security 异常

ExceptionTranslationFilter 可以将 AccessDeniedExceptionAuthenticationException 转入 HTTP 响应中。

ExceptionTranslationFilter 作为 Security Filters 中一员插入到 [[#FilterChainProxy]] 中。

  1. 首先,ExceptionTranslationFilter 调用 Filter.doFilter(requeset,response) 以执行其余应用程序。
  2. 如果用户未认证或者是 AuthenticationException ,那么开始认证。
    1. 清除 SecurityContextHolder
    2. HttpServletRequest 保存在 RequestCache 中。当用户成功认证后,RequestCache 用于重现原始请求。
    3. AuthenticationEntryPoint 用于来自客户端的请求凭证。例如,重定向到登录页面或者发送 WWW-Authenticate 头文件。
  3. 另外,如果是 AccessDenieException,那么将拒绝访问。将唤起 AccessDeniedHandler 处理拒绝访问。

ExceptionTranslationFilter 的伪代码如下:

try {
	filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException ex) {
	if (!authenticated || ex instanceof AuthenticationException) {
		startAuthentication();
	} else {
		accessDenied();
	}
}