Spring Security

Spring Security框架‌的主要作用是为基于Spring的企业应用系统提供声明式的安全访问控制解决方案。它是一个开源项目,旨在为Java应用程序提供强大和灵活的安全性解决方案,包括认证、授权、防护等多个方面。

认证与授权:Spring Security支持多种认证机制,如表单登录、HTTP基本认证OAuth2OpenID等,以及基于角色或权限的访问控制,提供细粒度的访问控制。

安全防护:提供多种防护措施,如防止会话固定攻击点击劫持跨站请求伪造等攻击,保护应用免受安全威胁。

集成性:与Spring框架以及其他第三方库和框架进行无缝集成,如Spring MVCThymeleafHibernate等,便于开发者在已有的技术栈中快速集成安全功能。

其他功能:还包括单点登录(SSO)功能,可以将多个应用程序集成到一个中央身份验证系统中,提供可定制的集成与其他Spring框架,如Spring MVC和Spring Boot。

SecurityFilterChain:SecurityFilterChain是Spring Security的核心组件之一,负责处理所有的HTTP请求。它由一系列的过滤器组成,这些过滤器在请求处理的不同阶段执行安全检查。

AuthenticationManager:身份验证的核心接口,负责验证用户的凭证。它会调用相应的AuthenticationProvider来实现具体的验证逻辑。

UserDetailsService:加载用户的特定数据。通过实现该接口,可以从数据库或其他数据源中获取用户信息。

SecurityContext:SecurityContext用于存储用户的身份信息(Authentication对象),它包含了用户的权限和角色信息,允许应用程序在整个请求周期内访问该信息。

工作流程:

  1. 客户端发送请求到服务器。
  2. SecurityFilterChain拦截请求,并检查用户的身份信息。
  3. 如果用户未认证,跳转到登录页面。
  4. 用户提交凭证,经过AuthenticationManager进行身份验证。
  5. 验证成功后,用户的身份信息被存储在SecurityContext中。
  6. 根据用户的角色和权限,决定是否允许访问请求的资源。

简单总结 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。

  1. HttpSecurityConfiguration目的是实例化HttpSecurity。
  2. 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大阶段:

  1. SecurityBuilder的每个子类将自己关注的功能都抽象化为接口SecurityConfigurer类型。每个SecurityConfigurer的实现类都会被添加至对应SecurityBuilder子类的属性configurers中。
  2. 执行抽象类AbstractConfiguredSecurityBuilder定义的模版方法init。
  3. 执行抽象类AbstractConfiguredSecurityBuilder定义的模版方法configure。该阶段就是为每一个功能配置一个Filter,并且将Filter添加至HttpSecurity集合属性filters中。
  4. 最后触发真正构建过程,其中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实例构建过程主要分为两大部分:

  1. 创建AuthenticationManagerBuilder实例,并且执行了SecurityBuilder定义的整个阶段,最终得到ProviderManager。
  2. 创建实例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类型的FilterChainProxyFilterChainProxy才是真正作为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全部功能。

授权

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值