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.@EnableWebSecurity

该注解是通过类SecurityAutoConfiguration间接引入的。

该注解引入类WebSecurityConfiguration、SpringWebMvcImportSelector、HttpSecurityConfiguration、OAuth2ImportSelector。

类WebSecurityConfiguration负责提供Spring Security框架存在的11个Filter。

类HttpSecurityConfiguration负责实例化HttpSecurity实例。


2.WebSecurityConfiguration

如下伪代码所示WebSecurityConfiguration核心功能就是完成Filter实例的创建。

java 复制代码
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {

	private WebSecurity webSecurity;

	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;

	private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();

	@Autowired(required = false)
	private HttpSecurity httpSecurity;

	@Autowired(required = false)
	void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
		this.securityFilterChains = securityFilterChains;
	}

	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		...
		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
			this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
		}
		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
			customizer.customize(this.webSecurity);
		}
		return this.webSecurity.build();
	}

	@Autowired(required = false)
	void setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) {
		this.webSecurityCustomizers = webSecurityCustomizers;
	}
}

但是必须借助SecurityFilterChain才能实现,最终被IOC容器管理的过滤器类型为FilterChainProxy

java 复制代码
public class FilterChainProxy extends GenericFilterBean {

    private List<SecurityFilterChain> filterChains;

    public FilterChainProxy(List<SecurityFilterChain> filterChains) {
        this.filterChains = filterChains;
    }
}

web服务处理请求过程中也是在过滤器FilterChainProxy内部实现11个具体Filter依次顺序执行。


2.1.HttpSecurity

类HttpSecurityConfiguration负责实例化Scope类型为prototype的HttpSecurity实例。此时的HttpSecurity其相关属性均为默认值。


2.2.SecurityFilterChain

SecurityFilterChain实例的创建需要用户显式的通过HttpSecurity类完成。

java 复制代码
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .cors(AbstractHttpConfigurer::disable)
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(registry -> registry
                    .requestMatchers(s).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();
}

Security框架内部提供了唯一的实现类DefaultSecurityFilterChain。如下伪代码所示filters集合默认情况下存在11个过滤器。

java 复制代码
public final class DefaultSecurityFilterChain implements SecurityFilterChain {

    private final RequestMatcher requestMatcher;
    private final List<Filter> filters;

    public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
        this.requestMatcher = requestMatcher;
        this.filters = new ArrayList<>(filters);
    }
}

按照顺序分别为:DisableEncodeUrlFilter、WebAsyncManagerIntegrationFilter、SecurityContextHolderFilter、HeaderWriterFilter、LogoutFilter、BearerTokenAuthenticationFilter、RequestAttributeAuthenticationFilter、SecurityContextHolderAwareRequestFilter、AnonymousAuthenticationFilter、ExceptionTranslationFilter、AuthorizationFilter。


3.SecurityFilterChain构建标准流程

抽象类AbstractConfiguredSecurityBuilder常见的子类为HttpSecurity & WebSecurity。

类HttpSecurity内部存在两个重要字段属性:元素类型为OrderedFilter的列表集合的filters & 元素类型为SecurityConfigurer的Map集合之configurers。

java 复制代码
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder{
	private final RequestMatcherConfigurer requestMatcherConfigurer;
	private List<OrderedFilter> filters = new ArrayList<>();
	private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
	private FilterOrderRegistration filterOrders = new FilterOrderRegistration();
	private AuthenticationManager authenticationManager;
}

字段filters维护的是上述所讲述的11种过滤器。

java 复制代码
public abstract class AbstractConfiguredSecurityBuilder {
	private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
	private final Map<Class<?>, Object> sharedObjects = new HashMap<>();
}

字段 configurers 维护的是上述所讲述的类SecurityConfigurer的子类。

类HttpSecurity的作用就是构建顺序Filter链的过程,每个Filter的具体功能被封装在SecurityConfigurer中。所以需要探明Filter的创建以及Filter与SecurityConfigurer之间的关系。

3.1.SecurityConfigurer子类创建过程

HttpSecurity构建相关功能时都会创建与之相对应的抽象类 AbstractHttpConfigurer的子类,诸如此类~[cors,CorsConfigurer],[authorizeHttpRequests,AuthorizeHttpRequestsConfigurer],[oauth2ResourceServer,OAuth2ResourceServerConfigurer],...,等。

每个SecurityConfigurer子类最终都会被添加至HttpSecurity的属性configurers中。

以AuthorizeHttpRequestsConfigurer为例:该SecurityConfigurer主要是基于请求URL验证请求的合法性。

如下所示registery通常利用requestMatchers设置相关过滤条件:

java 复制代码
//以下只是AuthorizeHttpRequestsConfigurer特有流程
.authorizeHttpRequests(registry -> registry
    //默认registry为AuthorizationManagerRequestMatcherRegistry
    .requestMatchers(s_AuthList).permitAll()
    .requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.OPTIONS)).permitAll()
    .requestMatchers(AntPathRequestMatcher.antMatcher("/**/*")).hasRole("role_user").anyRequest().authenticated()
)

requestMatchers方法接受三种类型的参数:字符串数组【路径正则表达式】、RequestMatcher类型的数组以及HttpMethod。返回值类型为AuthorizedUrl

3.1.1.AuthorizedUrl

3.2.Filter创建过程

java 复制代码
public abstract class AbstractConfiguredSecurityBuilder{
	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;
		}
	}
}

3.2.1.init


3.2.2. configure

以AuthorizeHttpRequestsConfigurer为例:过滤器AuthorizationFilter创建过程中默认得到的AuthorizationManager 的类型为:RequestMatcherDelegatingAuthorizationManager。

java 复制代码
public final class AuthorizeHttpRequestsConfigurer{
	public void configure(H http) {
        
		AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
		AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
		authorizationFilter.setAuthorizationEventPublisher(this.publisher);
		authorizationFilter.setShouldFilterAllDispatcherTypes(this.registry.shouldFilterAllDispatcherTypes);
		authorizationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
		http.addFilter(postProcess(authorizationFilter));
	}	
}

4.Filter

Spring Security本质上就是顺序Filter链执行过程,在执行目标方法之前或者在Servlet架构层完成对请求的验证过程。本章节以过滤器之AuthorizationFilter为例。

4.1.AuthorizationFilter

java 复制代码
public class AuthorizationFilter extends GenericFilterBean {
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain){

		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		...
		AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
		this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
		if (decision != null && !decision.isGranted()) {
            // 当前过滤器直接拒绝请求,响应头存在:401 Unauthorized内容
			throw new AccessDeniedException("Access Denied");
		}
        // 通过当前过滤器,执行下一个过滤器
		chain.doFilter(request, response);
	}
}
java 复制代码
public final class RequestMatcherDelegatingAuthorizationManager{
	private final List<RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>>> mappings;
	public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
			for (RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings) {
				RequestMatcher matcher = mapping.getRequestMatcher();
				MatchResult matchResult = matcher.matcher(request);
				if (matchResult.isMatch()) {
					AuthorizationManager<RequestAuthorizationContext> manager = mapping.getEntry();
					// 返回授权决定AuthorizationDecision
					return manager.check(authentication,
							new RequestAuthorizationContext(request, matchResult.getVariables()));
				}
			}
			return DENY;//说明当前过滤器直接拒绝当前请求
		}
}

通过当前过滤器的表现形式为:返回granted为true的AuthorizationDecision实例。该实例返回之前请求必须满足RequestMatcher条件 & AuthorizationManager条件。


java 复制代码
<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。

java 复制代码
<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包依赖。

java 复制代码
<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全部功能。

相关推荐
Kerwin要坚持日更2 小时前
Java小白一文讲清Java中集合相关的知识点(九)
java·开发语言
_Power_Y4 小时前
JavaSE:11、内部类
java·开发语言
小张同学(恩师白云)5 小时前
SpringDataJPA基础增删改查
java·数据库
尘浮生5 小时前
Java项目实战II基于Spring Boot的宠物商城网站设计与实现
java·开发语言·spring boot·后端·spring·maven·intellij-idea
Grey Zeng6 小时前
Java SE 23 新增特性
java·jdk·jdk新特性·jdk23
勤奋的小王同学~6 小时前
怎么修改mvn的java版本
java·开发语言
越过难题6 小时前
若依的使用
java
doc_wei6 小时前
Java小区物业管理系统
java·开发语言·spring boot·spring·毕业设计·课程设计·毕设
荆州克莱6 小时前
杨敏博士:基于法律大模型的智能法律系统
spring boot·spring·spring cloud·css3·技术
生产队队长6 小时前
SpringBoot2:web开发常用功能实现及原理解析-@ControllerAdvice实现全局异常统一处理
java·spring boot