相信了解过SpringSecurity或者是OAuth2的读者,会发现网上会有非常多的相关文章,或是纯概念的,或是带有demo的,无论是哪种类型的文章,本人去阅读之后,对于整个框架的概念还是一知半解,也仅仅是实现了某些功能、某些效果而已,若遇到某些问题时无从下手,只能去百度去Google。这是因为对于SpringSecurity和OAuth2的知识没有一个整体概念的把握,知识体系没有形成系统,遂决定写一个关于SpringSecurity和OAuth2的系列专栏,在构建自己知识体系的同时还希望能帮助有同样困惑的同学。
本专栏希望通过通俗易懂的语言来达到从入门概念、demo开始,由简入深,最终能达到深谙其底层源码的目的。
对于SpringSecurity,可以由其官网中的介绍了解到其概述:
对于SpringSecurity的特性,可以总结为以下几点:
在进行代码编写时,先得对SpringSecurity有一个整体上的认识,等进行coding时才能知道对应代码的对应作用。
在SpringSecurity继承的项目中,主要有四个核心的jar包:
由于spring-security-web.jar和spring-security-config.jar 都依赖了spring-security-core.jar,所以只需要导入spring-security-web.jar和spring-security-config.jar 即可。
本专栏系列文章相关demo版本:
SpringBoot: 2.1.14.RELEASE
org.springframework.security
spring-security-config
5.1.10.RELEASE
org.springframework.security
spring-security-web
5.1.10.RELEASE
不过,在SpringBoot中的spring-boot-starter-security中,就已经依赖好了spring-security-web.jar和spring-security-config.jar,直接拿来用即可。
既然SpringBoot都已经定义好了spring-boot-starter-security,那就用spring-boot-starter-security吧,这样做的好处是能够方便SpringBoot统一SpringSecurity的版本。
SpringBoot集成SpringSecurity需要配置几个配置文件,并且需要几个核心注解,下面来依次介绍这些注解。
@EnableWebSecurity是Spring Security用于启用Web安全的注解。典型的用法是该注解用在某个Web安全配置类上(实现了接口WebSecurityConfigurer或者继承自WebSecurityConfigurerAdapter)。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
@EnableGlobalAuthentication是包含在了@EnableWebSecurity注解中的,作用通过启用认证管理器(AuthenticationManager)来启用全局认证机制。
核心逻辑就在AuthenticationConfiguration类里。
在AuthenticationConfiguration在SpringSecurity中扮演者非常重要的作用,它内部包含了AuthenticationManager用于核心的认证工作。接下来将会重点讲解该类。
Spring Security默认是禁用注解的,要想开启注解,需要在继承WebSecurityConfigurerAdapter的类上加@EnableGlobalMethodSecurity注解,来判断用户对某个控制层的方法是否具有访问权限。
除此之外,还可以在@EnableGlobalMethodSecurity中添加几个属性。
在SpringSecurity中,有着许多的组件包括AuthenticationManager、AccessDesicionManager和UsernamePasswordAuthenticationFilter等。
对于SpringSecurity来说,最大的两个问题就是:认证(Authentication,即你是谁?)和授权(Authorization,允许你做什么?)。SpringSecurity框架旨在将认证从授权中剥离出来,并也有适用于二者的策略和可扩展的设计。
在SpringSecurity中,用于认证的主要接口是AuthenticationManager,它只有一个方法:
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
AuthenticationManger最普遍的实现类是ProviderManager,而ProviderManager会将认证委托给AuthenticationProvider。
AuthenticationProvider接口和AuthenticationManager相似,但是它有一个额外的方法允许查询它支持的Authentication方式:
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class> authentication);
}
一旦认证成功,我们就可以进行授权了,它核心的策略就是AccessDecisionManager。同样的,它将授权逻辑全部委托给AccessDecisionVoter来实现。
一个AccessDecisionVoter考虑一个Authentication(代表一个Principal)和一个被ConfigAttributes装饰的安全对象,这里的ConfigAttributes就是一个包含了URL以及这个URL该有权限的对象的集合。
boolean supports(ConfigAttribute attribute);
boolean supports(Class> clazz);
int vote(Authentication authentication, S object,
Collection attributes);
现在,已经拥有了认证和授权组件了,那么一个HTTP请求进入SpringSecurity应用时,经过过滤器链中都发生了哪些逻辑?接下来就看下SpringSecurity中过滤器链底层都发生了什么。
除了认证和授权外,SpringSecurity的另外一个核心就是Servlet的Filter来实现的。先简单回顾下Servlet中Filter的调用原理。
下图展示了处理单个HTTP请求的经典分层结构图:
客户端向服务器发起请求,然后Servlet容器(Tomcat)会根据URI来决定哪个Filter和哪个Servlet适用于这个请求,一个Servlet最多处理一个请求,过滤器是链式的,它们是有顺序的。事实上一个过滤器可以否决接下来的过滤器,上一个过滤器请求处理完就会动过调用Filter的doFilter()向下传递请求,知道请求最终到达Servlet处理完毕后,再过滤器以相反的顺序调用,再以Response返回给客户端。
回归正题,SpringSecurity在过滤器链中扮演的就是一个Filter,其类型是FilterChainProxy。但它又不是一个普通的Filter,因为FilterChainProxy中包含了额外的过滤器,每个过滤器都发挥特殊的作用。下面用一张图展示下FliterChainProxy中包含的过滤器链。
在SpringBoot中,SpringSecurity的FilterChainProxy是以bean的形式注入到Spring容器中的,并且它是默认配置,所以在每次请求中都会存在,所以在SpringSecurity保护的应用中,每次请求都会经过FilterChainProxy。
本片文章粗略的讲解了SpringSecurity里的几个核心概念,包括:核心注解、认证和授权的核心组件以及SpringSecurity中的FilterChainProxy,整个SpringSecurity框架就是围绕着这几个核心概念运行,下面几章将会深入分析每个核心概念的底层运行机制。
作者本人github中开发并维护着一个后端基于Dubbo+SpringBoot+SpringSecurity+OAuth2而前端基于Vue的前后端分离电商项目,其中还维护着一个实战专栏用于详细记录开发该电商项目时遇到的技术难点以及解决方案,感兴趣读者可以前往查看,后端地址如下:
https://github.com/coderbruis/Distributed-mall另外,本篇博文已收录至:https://github.com/coderbruis/JavaSourceCodeLearning