Spring Security框架的主要作用是为基于Spring的企业应用系统提供声明式的安全访问控制解决方案。它是一个开源项目,旨在为Java应用程序提供强大和灵活的安全性解决方案,包括认证、授权、防护等多个方面。
认证与授权:Spring Security支持多种认证机制,如表单登录、HTTP基本认证、OAuth2、OpenID等,以及基于角色或权限的访问控制,提供细粒度的访问控制。
安全防护:提供多种防护措施,如防止会话固定攻击、点击劫持、跨站请求伪造等攻击,保护应用免受安全威胁。
集成性:与Spring框架以及其他第三方库和框架进行无缝集成,如Spring MVC、Thymeleaf、Hibernate等,便于开发者在已有的技术栈中快速集成安全功能。
其他功能:还包括单点登录(SSO)功能,可以将多个应用程序集成到一个中央身份验证系统中,提供可定制的集成与其他Spring框架,如Spring MVC和Spring Boot。
SecurityFilterChain:SecurityFilterChain是Spring Security的核心组件之一,负责处理所有的HTTP请求。它由一系列的过滤器组成,这些过滤器在请求处理的不同阶段执行安全检查。
AuthenticationManager:身份验证的核心接口,负责验证用户的凭证。它会调用相应的AuthenticationProvider来实现具体的验证逻辑。
UserDetailsService:加载用户的特定数据。通过实现该接口,可以从数据库或其他数据源中获取用户信息。
SecurityContext:SecurityContext用于存储用户的身份信息(Authentication对象),它包含了用户的权限和角色信息,允许应用程序在整个请求周期内访问该信息。
工作流程:
- 客户端发送请求到服务器。
- SecurityFilterChain拦截请求,并检查用户的身份信息。
- 如果用户未认证,跳转到登录页面。
- 用户提交凭证,经过AuthenticationManager进行身份验证。
- 验证成功后,用户的身份信息被存储在SecurityContext中。
- 根据用户的角色和权限,决定是否允许访问请求的资源。
简单总结 Spring Security 流程即为:构建 Filter 链、执行Filter 链。Filter 链的构建依赖用户显式创建 SecurityFilterChain 实例,最后被 WebSecurityConfiguration 包装为FilterChainProxy。
1. SecurityAutoConfiguration
当前自动装配类通过@Import引入类SpringBootWebSecurityConfiguration以及SecurityProperties。
如果用户没有显式定义SecurityFilterChain,则Security框架本身引入默认的bean:
class SpringBootWebSecurityConfiguration {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
return http.build();
}
}
默认方式导致的结果:针对任何请求都会定位到其web自带的登录页面,登录成功后继续访问目标地址。
2.@EnableWebSecurity
该注解通过@Import引入类WebSecurityConfiguration、SpringWebMvcImportSelector、HttpSecurityConfiguration、OAuth2ImportSelector。
- HttpSecurityConfiguration目的是实例化HttpSecurity。
- WebSecurityConfiguration目的是实例化WebSecurity。
使用Spring Security框架时最重要的是用户显式构建SecurityFilterChain,示例如下:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(registry -> registry
.requestMatchers(Arrays.asList("/","/account/token").toArray(new String[2])).permitAll()
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.OPTIONS)).permitAll()
.requestMatchers(AntPathRequestMatcher.antMatcher("/**/*")).hasRole("role_user")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2Configurer -> oauth2Configurer.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(jwt -> {
Map<String, Collection<String>> realmAccess = jwt.getClaim("realm_access");
Collection<String> roles = realmAccess.get("roles");
var grantedAuthorities = roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.toList();
return new JwtAuthenticationToken(jwt, grantedAuthorities);
})))
;
httpSecurity.exceptionHandling(handleConfig -> handleConfig.accessDeniedHandler(m_AccessDeniedHandler));
return httpSecurity.build();
}
按照顺序分别为:DisableEncodeUrlFilter、WebAsyncManagerIntegrationFilter、SecurityContextHolderFilter、HeaderWriterFilter、LogoutFilter、BearerTokenAuthenticationFilter、RequestAttributeAuthenticationFilter、SecurityContextHolderAwareRequestFilter、AnonymousAuthenticationFilter、ExceptionTranslationFilter、AuthorizationFilter。请求在不同过滤器流转过程中通过应用上下文SecurityContex传递下游过滤器关注的行为。
2.1.SecurityBuilder
接口SecurityBuilder有个重要的抽象实现类AbstractConfiguredSecurityBuilder,Security框架默认为该抽象类创建了三个子类:HttpSecurity & WebSecurity & AuthenticationManagerBuilder。
AbstractConfiguredSecurityBuilder定义的标准流程中存在两个重要阶段 init & configure。这两个重要阶段内部是执行集合configurers中所有SecurityConfigurer对应的 init & configure方法。当然,接口SecurityConfigurer中同样标准化了init & configure 两阶段。
上述三个子类构建过程中主要历经4大阶段:
- SecurityBuilder的每个子类将自己关注的功能都抽象化为接口SecurityConfigurer类型。每个SecurityConfigurer的实现类都会被添加至对应SecurityBuilder子类的属性configurers中。
- 执行抽象类AbstractConfiguredSecurityBuilder定义的模版方法init。
- 执行抽象类AbstractConfiguredSecurityBuilder定义的模版方法configure。该阶段就是为每一个功能配置一个Filter,并且将Filter添加至HttpSecurity集合属性filters中。
- 最后触发真正构建过程,其中HttpSecurity最重要的是每个Filter基于Order排序。
三者通过模板设计模式完成构建过程中涉及的2、3、4阶段,具体如下所示:
public abstract class AbstractConfiguredSecurityBuilder{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
protected final O doBuild() throws Exception {
synchronized (this.configurers) {
this.buildState = BuildState.INITIALIZING;
beforeInit();
// 触发重写过该init方法的SecurityConfigurer子类
init();
this.buildState = BuildState.CONFIGURING;
beforeConfigure();
// 触发重写过该configure方法的SecurityConfigurer子类,创建与当前SecurityConfigurer对应的Filter
configure();
this.buildState = BuildState.BUILDING;
O result = performBuild();
this.buildState = BuildState.BUILT;
return result;
}
}
}
其中HttpSecurity构建过程得到的结果是DefaultSecurityFilterChain & WebSecurity则是返回被IOC容器管理的FilterChainProxy实例bean & AuthenticationManagerBuilder得到的结果为ProviderManager。
2.1.1. HttpSecurityConfiguration
HttpSecurity实例构建过程主要分为两大部分:
- 创建AuthenticationManagerBuilder实例,并且执行了SecurityBuilder定义的整个阶段,最终得到ProviderManager。
- 创建实例HttpSecurity,注意此时HttpSecurity只是将关注的功能抽象化为SecurityConfigurer,并没有触发SecurityBuilder定义的2、3、4阶段。
HttpSecurity实例的创建如下所示:
class HttpSecurityConfiguration {
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
HttpSecurity httpSecurity() throws Exception {
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor, passwordEncoder);
// 通过 authenticationManager() 方法得到ProviderManager
authenticationBuilder.parentAuthenticationManager(authenticationManager());
authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
WebAsyncManagerIntegrationFilter webAsyncManagerIntegrationFilter = new WebAsyncManagerIntegrationFilter();
webAsyncManagerIntegrationFilter.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
// @formatter:off
http
.csrf(withDefaults())// 将csrf功能抽象为CsrfConfigurer
.addFilter(webAsyncManagerIntegrationFilter)
.exceptionHandling(withDefaults())// ExceptionHandlingConfigurer
.headers(withDefaults())//HeadersConfigurer
.sessionManagement(withDefaults())//SessionManagementConfigurer
.securityContext(withDefaults())//SecurityContextConfigurer
.requestCache(withDefaults())
.anonymous(withDefaults())//AnonymousConfigurer
.servletApi(withDefaults())//ServletApiConfigurer
.apply(new DefaultLoginPageConfigurer<>());
http.logout(withDefaults());//LogoutConfigurer
// @formatter:on
applyDefaultConfigurers(http);
return http;
}
}
Security框架内部提供了HttpSecurity 涉及的2、3、4阶段完整实现,但是通常这三个阶段由用户自定义完成:方便用户在HttpSecurity现有的功能之上【SecurityConfigurer】添加用户关注的新功能。
以SecurityContextConfigurer为例:在AuthenticationManagerBuilder的定义的configure阶段为HttpSecurity初始化一个过滤器SecurityContextPersistenceFilter,该过滤器处理请求过程中会为当前请求构建一个上下文:SecurityContext。
2.1.2. WebSecurityConfiguration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
...
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
break;
}
}
}
for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
customizer.customize(this.webSecurity);
}
//webSecurity构建过程:AbstractConfiguredSecurityBuilder定义的2、3、4阶段
return this.webSecurity.build();
}
// 该注解引入用户自定义的 SecurityFilterChain
@Autowired(required = false)
void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
this.securityFilterChains = securityFilterChains;
}
@Autowired(required = false)
void setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) {
this.webSecurityCustomizers = webSecurityCustomizers;
}
}
WebSecurityConfiguration借助SecurityFilterChain将众多过滤器抽象为Filter类型的FilterChainProxy。FilterChainProxy才是真正作为Servlet三大组件中的Filter组件,在处理请求过程中在其内部完成所有Filter遍历顺序执行。
2.2.SecurityFilterChain
Security架构中默认提供了唯一实现类之DefaultSecurityFilterChain。
SecurityFilterChain可以认定为HttpSecurity 与 WebSecurity之间构建联系的桥梁:前后负责初始化Filter实例,后者负责将前者Filter抽象化至FilterChainProxy中,并被IOC容器托管处理。
3.SecurityContext
由SecurityContextConfigurer在其configure阶段创建的过滤器SecurityContextHolderFilter
负责创建请求上下文SecurityContext:
public interface SecurityContext extends Serializable {
Authentication getAuthentication();
void setAuthentication(Authentication authentication);
}
如下所示在创建SecurityContextHolderFilter时涉及两个核心改变:SecurityContextRepository & SecurityContextHolderStrategy。
public final class SecurityContextConfigurer{
public void configure(H http) {
SecurityContextRepository securityContextRepository = getSecurityContextRepository();
SecurityContextHolderFilter securityContextHolderFilter = postProcess(new SecurityContextHolderFilter(securityContextRepository));
securityContextHolderFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
http.addFilter(securityContextHolderFilter);
}
}
其中,SecurityContextRepository是指创建跟请求相关的SecurityContext的策略。
接口SecurityContextRepository在SpringSecurity框架内默认提供了4种实现类:
- NullSecurityContextRepository。
- DelegatingSecurityContextRepository。
- RequestAttributeSecurityContextRepository。
- HttpSessionSecurityContextRepository。
其中DelegatingSecurityContextRepository是对RequestAttributeSecurityContextRepository、HttpSessionSecurityContextRepository的代理,也是SpringSecurity框架内默认提供的方式。
其中, SecurityContextHolderStrategy是指SecurityContext维护或者持有策略,在SecurityContextHolder内部通过静态代码块完成所有策略的初始化,包含:
- ThreadLocalSecurityContextHolderStrategy,默认策略。
- InheritableThreadLocalSecurityContextHolderStrategy。
- GlobalSecurityContextHolderStrategy。
public abstract class AbstractHttpConfigurer{
protected SecurityContextHolderStrategy getSecurityContextHolderStrategy() {
...
return SecurityContextHolder.getContextHolderStrategy();;
}
}
AbstractHttpConfigurer内部提供了通过SecurityContextHolder获取SecurityContextHolderStrategy策略的具体实施方案。
每个AbstractHttpConfigurer的子类都可以通过该方式得到同一个SecurityContextHolderStrategy。
好多AbstractHttpConfigurer子类在HttpSecurity构建的configure阶段都会为对应的过滤器设置SecurityContextHolderStrategy。包含:ExceptionHandlingConfigurer、SecurityContextConfigurer、AnonymousConfigurer、ServletApiConfigurer等。
3.1.获取延迟式的 SecurityContext
public class SecurityContextHolderFilter extends GenericFilterBean {
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain){
...
// 通过延迟加载手法延迟创建SecurityContext实例
Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
// 将Supplier类型的SecurityContext根据持有策略选择Holder持有当前SecurityContext
this.securityContextHolderStrategy.setDeferredContext(deferredContext);
chain.doFilter(request, response);
}
}
通过SecurityContextRepository创建SecurityContext时得到的是Supplier类型的defferContext。这种延迟加载的手法是指此时并不会真正创建SecurityContext实例,而是在真正使用SecurityContext时才考虑创建。
public final class DelegatingSecurityContextRepository implements SecurityContextRepository {
private final List<SecurityContextRepository> delegates;
public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
DeferredSecurityContext deferredSecurityContext = null;
for (SecurityContextRepository delegate : this.delegates) {
if (deferredSecurityContext == null) {
deferredSecurityContext = delegate.loadDeferredContext(request);
}
else {
DeferredSecurityContext next = delegate.loadDeferredContext(request);
deferredSecurityContext = new DelegatingDeferredSecurityContext(deferredSecurityContext, next);
}
}
return deferredSecurityContext;
}
}
从上述章节得知:属性delegates中存在两个属性RequestAttributeSecurityContextRepository、HttpSessionSecurityContextRepository,分别表示对RequestAttribute、HttpSession相关SecurityContext的代理。最终返回的实例SecurityContextRepository为包含上述两种SecurityContext代理的DelegatingDeferredSecurityContext。
4.Spring Security框架默认内置的SecurityContext
Spring Security框架内部默认存在两个过滤器设置SecurityContext,在过滤器责任链中按照先后顺序分别为SecurityContextHolderFilter、AnonymousAuthenticationFilter。
public class AnonymousAuthenticationFilter{
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain){
// 此处获取到SecurityContextHolderFilter添加的deferredContext
Supplier<SecurityContext> deferredContext = this.securityContextHolderStrategy.getDeferredContext();
this.securityContextHolderStrategy.setDeferredContext(defaultWithAnonymous((HttpServletRequest) req, deferredContext));
chain.doFilter(req, res);
}
private Supplier<SecurityContext> defaultWithAnonymous(HttpServletRequest request,
Supplier<SecurityContext> currentDeferredContext) {
// 直接返回 当前过滤器 添加的 deferredContext
return SingletonSupplier.of(() -> {
//SecurityContextHolderFilter添加的deferredContext
SecurityContext currentContext = currentDeferredContext.get();
return defaultWithAnonymous(request, currentContext);
});
}
private SecurityContext defaultWithAnonymous(HttpServletRequest request, SecurityContext currentContext) {
// 如果 SecurityContextHolderFilter添加的deferredContext返回Authentication,则不需要Anonymous过滤器生成的Authentication
Authentication currentAuthentication = currentContext.getAuthentication();
if (currentAuthentication == null) {
Authentication anonymous = createAuthentication(request);
SecurityContext anonymousContext = this.securityContextHolderStrategy.createEmptyContext();
anonymousContext.setAuthentication(anonymous);
return anonymousContext;
}
return currentContext;
}
}
当真正执行延迟策略通过 SecurityContext 获取 Authentication 时,首先从 SecurityContextHolderFilter 中获取Authentication,存在则优先返回,否则返回Anonymous过滤器生成的Authentication。
5.ThreadLocalSecurityContextHolderStrategy
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<Supplier<SecurityContext>> contextHolder = new ThreadLocal<>();
@Override
public Supplier<SecurityContext> getDeferredContext() {
Supplier<SecurityContext> result = contextHolder.get();
if (result == null) {
SecurityContext context = createEmptyContext();
result = () -> context;
contextHolder.set(result);
}
return result;
}
@Override
public void setDeferredContext(Supplier<SecurityContext> deferredContext) {
Supplier<SecurityContext> notNullDeferredContext = () -> {
SecurityContext result = deferredContext.get();
return result;
};
contextHolder.set(notNullDeferredContext);
}
}
如上伪代码所示:ThreadLocal维护的SecurityContext均为延迟式的Supplier函数式接口。
6.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
<version>3.3.3</version>
</dependency>
依赖spring-security-oauth2-authorization-server完全覆盖spring-boot-starter-oauth2-authorization-server。
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>1.3.2</version>
</dependency>
与 spring-security-oauth2-authorization-server 相比,spring-boot-starter-security依赖多了一个spring-aop的jar包依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.3.3</version>
</dependency>
综上所述,spring-security-oauth2-authorization-server 、spring-boot-starter-security两个依赖完全提供spring-security全部功能。