Spring Security 请求全过程解析

简介: 《读尽源码》

Spring Security 是一款基于 Spring 的安全框架,主要包含认证和授权两大安全模块,和另外一款流行的安全框架 Apache Shiro 相比,它拥有更为强大的功能。Spring Security 也可以轻松的自定义扩展以满足各种需求,并且对常见的 Web 安全攻击提供了防护支持。如果你的 Web 框架选择的是 Spring,那么在安全方面 Spring Security 会是一个不错的选择。

这里我们使用 Spring Boot 来集成 Spring Security,Spring Boot 版本为2.5.3,Spring Security 版本为5.5.1。

开启 Spring Security

使用 IDEA 创建一个 Spring Boot 项目,然后引入spring-boot-starter-security:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.projectlombok:lombok:1.18.8'
    annotationProcessor 'org.projectlombok:lombok:1.18.8'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
}Copy to clipboardErrorCopied

接下来我们创建一个HelloController,对外提供一个/hello服务:

@RestController
public class HelloController {
    @GetMapping("hello")
    public String hello() {
        return "hello world";
    }
}Copy to clipboardErrorCopied

这时候我们直接启动项目,访问http://localhost:8080/hello,可以看到页面跳转到一个登陆页面:

默认的用户名为 user,密码由 Sping Security 自动生成,回到 IDEA 的控制台,可以找到密码信息:

Using generated security password: 4f06ba04-37e9-4bdd-a085-3305260da0d6Copy to clipboardErrorCopied

输入用户名 user,密码 4f06ba04-37e9-4bdd-a085-3305260da0d6 后,我们便可以成功访问/hello接口。

基本原理

Spring Security 默认为我们开启了一个简单的安全配置,下面让我们来了解其原理。

当 Spring Boot 项目配置了 Spring Security 后,Spring Security 的整个加载过程如下图所示:

而当我们访问http://localhost:8080/hello时,代码的整个执行过程如下图所示:

如上图所示,Spring Security 包含了众多的过滤器,这些过滤器形成了一条链,所有请求都必须通过这些过滤器后才能成功访问到资源。

下面我们通过 debug 来验证这个过程:

首先,通过前面可以知道,当有请求来到时,最先由DelegatingFilterProxy负责接收,因此在DelegatingFilterProxy的doFilter()的首行打上断点:

接着DelegatingFilterProxy会将请求委派给FilterChainProxy进行处理,在FilterChainProxy的首行打上断点:

FilterChainProxy会在doFilterInternal()中生成一个内部类VirtualFilterChain的实例,以此来调用 Spring Security 的整条过滤器链,在VirtualFilterChain的doFilter()首行打上断点:

接下来VirtualFilterChain会通过currentPosition依次调用存在additionalFilters中的过滤器,其中比较重要的几个过滤器有:UsernamePasswordAuthenticationFilter、DefaultLoginPageGeneratingFilter、AnonymousAuthenticationFilter、ExceptionTranslationFilter、FilterSecurityInterceptor,我们依次在这些过滤器的doFilter()的首行打上断点:

准备完毕后,我们启动项目,然后访问http://localhost:8080/hello,程序首先跳转到DelegatingFilterProxy的断点上:

此时delegate还是 null 的,接下来依次执行代码,可以看到delegate最终被赋值一个FilterChainProxy的实例:

接下来程序依次跳转到FilterChainProxy的doFilter()和VirtualFilterChain的doFilter()中:

接着程序跳转到AbstractAuthenticationProcessingFilter(UsernamePasswordAuthenticationFilter的父类)的doFilter()中,通过requiresAuthentication()判定为 false(是否是 POST 请求):

接着程序跳转到DefaultLoginPageGeneratingFilter的doFilter()中,通过isLoginUrlRequest()判定为 false(请求路径是否是/login):

接着程序跳转到AnonymousAuthenticationFilter的doFilter()中,由于是首次请求,此时SecurityContextHolder.getContext().getAuthentication()为 null,因此会生成一个AnonymousAuthenticationToken的实例:

接着程序跳转到ExceptionTranslationFilter的doFilter()中,ExceptionTranslationFilter负责处理FilterSecurityInterceptor抛出的异常,我们在 catch 代码块的首行打上断点:

接着程序跳转到FilterSecurityInterceptor的doFilter()中,依次执行代码后程序停留在其父类(AbstractSecurityInterceptor)的attemptAuthorization()中:

accessDecisionManager是AccessDecisionManager(访问决策器)的实例,AccessDecisionManager主要有 3 个实现类:AffirmativeBased(一票通过),ConsensusBased(少数服从多数)、UnanimousBased(一票否决),此时AccessDecisionManager的的实现类是AffirmativeBased,我们可以看到程序进入AffirmativeBased的decide()中:

从上图可以看出,决策的关键在voter.vote(authentication, object, configAttributes)这句代码上,通过跟踪调试,程序最终进入AuthenticationTrustResolverImpl的isAnonymous()中:

isAssignableFrom()判断前者是否是后者的父类,而anonymousClass被固定为AnonymousAuthenticationToken.class,参数authentication由前面AnonymousAuthenticationFilter可以知道是AnonymousAuthenticationToken的实例,因此isAnonymous()返回 true,FilterSecurityInterceptor抛出AccessDeniedException异常,程序返回ExceptionTranslationFilter的 catch 块中:

接着程序会依次进入DelegatingAuthenticationEntryPoint、LoginUrlAuthenticationEntryPoint中,最后由LoginUrlAuthenticationEntryPoint的commence()决定重定向到/login:

后续对/login的请求同样会经过之前的执行流程,在DefaultLoginPageGeneratingFilter的doFilter()中,通过isLoginUrlRequest()判定为 true(请求路径是否是/login),直接返回login.html,也就是我们开头看到的登录页面。

当我们输入用户名和密码,点击Sign in,程序来到AbstractAuthenticationProcessingFilter的doFilter()中,通过requiresAuthentication()判定为 true(是否是 POST 请求),因此交给其子类UsernamePasswordAuthenticationFilter进行处理,UsernamePasswordAuthenticationFilter会将用户名和密码封装成一个UsernamePasswordAuthenticationToken的实例并进行校验,当校验通过后会将请求重定向到我们一开始请求的路径:/hello。

后续对/hello的请求经过过滤器链时就可以一路开绿灯直到最终交由HelloController返回"Hello World"。

相关文章
|
10月前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
10月前
|
缓存 安全 Java
Spring Security通用权限管理模型解析
Spring Security作为Spring生态的核心安全框架,结合RBAC与ACL权限模型,基于IoC与AOP构建灵活、可扩展的企业级权限控制体系,涵盖认证、授权流程及数据库设计、性能优化等实现策略。
713 0
|
10月前
|
缓存 安全 Java
Spring Security权限管理解析
Spring Security是Spring生态中的核心安全框架,采用认证与授权分离架构,提供高度可定制的权限管理方案。其基于过滤器链实现认证流程,通过SecurityContextHolder管理用户状态,并结合RBAC模型与动态权限决策,支持细粒度访问控制。通过扩展点如自定义投票器、注解式校验与前端标签,可灵活适配多租户、API网关等复杂场景。结合缓存优化与无状态设计,适用于高并发与前后端分离架构。
706 0
|
10月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
2981 0
|
10月前
|
SQL Java 数据库连接
Spring Data JPA 技术深度解析与应用指南
本文档全面介绍 Spring Data JPA 的核心概念、技术原理和实际应用。作为 Spring 生态系统中数据访问层的关键组件,Spring Data JPA 极大简化了 Java 持久层开发。本文将深入探讨其架构设计、核心接口、查询派生机制、事务管理以及与 Spring 框架的集成方式,并通过实际示例展示如何高效地使用这一技术。本文档约1500字,适合有一定 Spring 和 JPA 基础的开发者阅读。
918 0
|
9月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
9月前
|
缓存 监控 Java
《深入理解Spring》拦截器(Interceptor)——请求处理的艺术
Spring拦截器是Web开发中实现横切关注点的核心组件,基于AOP思想,可在请求处理前后执行日志记录、身份验证、权限控制等通用逻辑。相比Servlet过滤器,拦截器更贴近Spring容器,能访问Bean和上下文,适用于Controller级精细控制。通过实现`HandlerInterceptor`接口的`preHandle`、`postHandle`和`afterCompletion`方法,可灵活控制请求流程。结合配置类注册并设置路径匹配与执行顺序,实现高效复用与维护。常用于认证鉴权、性能监控、统一异常处理等场景,提升应用安全性与可维护性。
|
9月前
|
缓存 安全 Java
《深入理解Spring》过滤器(Filter)——Web请求的第一道防线
Servlet过滤器是Java Web核心组件,可在请求进入容器时进行预处理与响应后处理,适用于日志、认证、安全、跨域等全局性功能,具有比Spring拦截器更早的执行时机和更广的覆盖范围。
|
9月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。

推荐镜像

更多
  • DNS