简介
Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。
WEB 安全方面的两个主要区域是“认证”和“授权”(或者访问控制),一般来说,Web 应用的安全性包括用户认(Authentication)和用户授权(Authorization)两个部分,这两点也是 Spring Security 重要核心功能。
历史
“Spring Security 开始于 2003 年年底,““spring 的 acegi 安全系统”。 起因是 Spring 开发者邮件列表中的一个问题,有人提问是否考虑提供一个基于 spring 的安全实现。
Spring Security 以“The Acegi Secutity System for Spring” 的名字始于 2013 年晚些时候。一个问题提交到 Spring 开发者的邮件列表,询问是否已经有考虑一个机遇 Spring 的安全性社区实现。那时候 Spring 的社区相对较小(相对现在)。实际上 Spring 自己在 2013 年只是一个存在于 ScourseForge 的项目,这个问题的回答是一个值得研究的领域,虽然目前时间的缺乏组织了我们对它的探索。
6 上新增的功能
-
Core
- gh-12233 - 安全授权管理器允许自定义底层授权管理器
- GH-12231 - 添加颁发机构集合授权管理器
-
OAuth 2.0
- gh-12604 - 支持 AuthnRequestSigned 元数据属性
- gh-12846 - 元数据支持多个实体和实体描述符
- gh-11828 - (文档) - 将 saml2 元数据添加到 DSL
- gh-12843 - (文档) - 允许从注销请求中推断信赖方
- gh-10243 - (文档) - 允许从 SAML 响应推断信赖方
- gh-12842 - 添加信赖方注册占位符解析组件
- gh-12845 - 支持在已注销后发出注销响应
-
Observability
- gh-12534 - 自定义身份验证和授权观察约定
-
Web
- gh-12751 - 添加请求匹配器工厂类
- gh-12847 - 通过 And 和 OrRequestMatcher 传播变量
-
Docs
- GH-13088 - (文档) - 重新访问授权文档
- gh-12681 - (文档) - 重新访问会话管理文档
- gh-13062 - (文档) - 重新访问注销文档
- gh-13089 - 重温 CSRF 文档
相关概念
-
主体
英文单词:principal使用系统的用户或设备或从其他系统远程登录的用户等等。简单说就是谁使用系统谁就是主体。
-
认证
英文单词:authentication
权限管理系统确认一个主体的身份,允许主体进入系统。简单说就是“主体”证明自己是谁。
笼统的认为就是以前所做的登录操作。 -
授权
英文单词:authorization
将操作系统的“权力”“授予”“主体”,这样主体就具备了操作系统中特定功能的能力。
注解使用
- @Secured:判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。
- @PreAuthorize 注解适合进入方法前的权限验证,
- @PostAuthorize 在方法执行后再进行权限验证,适合验证带有返回值的权限
- @PostFilter 过滤注解,权限验证之后对数据进行过滤 留下满足要求的数据
- @PreFilter 进入控制器之前对数据进行过滤
https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html
责任链
spring security 采用的时责任链的设计模式,再了解他之前,先了解下此种设计模式
责任链设计模式是一种行为型设计模式,它将请求的发送者和接收者解耦,将多个处理对象连成一条责任链,依次处理请求,直到请求被处理或者到达责任链的末尾。该模式常用于日志记录、权限验证、请求过滤等场景。
角色介绍
- 抽象处理者(Handler)
抽象处理者定义了一个处理请求的接口,并保存下一个处理对象的引用。它可以是抽象类或接口。
public abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handleRequest(Request request);
}
- 具体处理者(ConcreteHandler)
具体处理者实现了抽象处理者的接口,并处理它所负责的请求。如果不能处理该请求,则将请求传递给下一个处理对象。
public class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE_A) {
// 处理请求
} else {
// 无法处理该请求,将请求传递给下一个处理对象
if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
// 如果没有下一个处理对象,则输出日志
System.out.println("No handler available for request.");
}
}
}
}
- 客户端(Client)
客户端创建请求对象,并将其发送给第一个处理对象。客户端可以根据需要自定义处理对象的顺序,也可以动态地增加或删除处理对象。
public class Client {
public static void main(String[] args) {
// 创建请求对象
Request request = new Request(RequestType.TYPE_A, "Request A");
// 创建处理对象
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
Handler handlerC = new ConcreteHandlerC();
// 构建责任链
handlerA.setNextHandler(handlerB);
handlerB.setNextHandler(handlerC);
// 发送请求
handlerA.handleRequest(request);
}
}
SpringSecurity 的过滤器
Spring Security 的过滤器链是配置在 SpringMVC 的核心组件 DispatcherServlet 运行之前。也就是说,请求通过 Spring Security 的所有过滤器, 不意味着能够正常访问资源,该请求还需要通过 SpringMVC 的拦截器链。
Security Filter 通过 SecurityFilterChain 被插入到 FilterChainProxy 中。可以通过 FilterOrderRegistration 查看过滤器的顺序:28 个内置过滤器
FilterOrderRegistration() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
put(DisableEncodeUrlFilter.class, order.next());
put(ForceEagerSessionCreationFilter.class, order.next());
put(ChannelProcessingFilter.class, order.next());
order.next(); // gh-8105
put(WebAsyncManagerIntegrationFilter.class, order.next());
put(SecurityContextHolderFilter.class, order.next());
put(SecurityContextPersistenceFilter.class, order.next());
put(HeaderWriterFilter.class, order.next());
put(CorsFilter.class, order.next());
put(CsrfFilter.class, order.next());
put(LogoutFilter.class, order.next());
this.filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
order.next());
this.filterToOrder.put(
"org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter",
order.next());
put(X509AuthenticationFilter.class, order.next());
put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());
this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
order.next());
this.filterToOrder.put(
"org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",
order.next());
put(UsernamePasswordAuthenticationFilter.class, order.next());
order.next(); // gh-8105
put(DefaultLoginPageGeneratingFilter.class, order.next());
put(DefaultLogoutPageGeneratingFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
put(DigestAuthenticationFilter.class, order.next());
this.filterToOrder.put(
"org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter",
order.next());
put(BasicAuthenticationFilter.class, order.next());
put(RequestCacheAwareFilter.class, order.next());
put(SecurityContextHolderAwareRequestFilter.class, order.next());
put(JaasApiIntegrationFilter.class, order.next());
put(RememberMeAuthenticationFilter.class, order.next());
put(AnonymousAuthenticationFilter.class, order.next());
this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
order.next());
put(SessionManagementFilter.class, order.next());
put(ExceptionTranslationFilter.class, order.next());
put(FilterSecurityInterceptor.class, order.next());
put(AuthorizationFilter.class, order.next());
put(SwitchUserFilter.class, order.next());
}
15 个过滤器进行说明
-
(1)WebAsyncManagerIntegrationFilter:将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成。
-
(2)SecurityContextHolderFilter/SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中,然后在该次请求处理完成之后,将 SecurityContextHolder 中关于这次请求的信息存储到一个“仓储”中,然后将 SecurityContextHolder 中的信息清除,例如在 Session 中维护一个用户的安全信息就是这个过滤器处理的。所有过滤器的入口;
-
(3)HeaderWriterFilter:用于将头信息加入响应中。
-
(4)CsrfFilter:用于处理跨站请求伪造。
-
(5)LogoutFilter:用于处理退出登录。
-
(6)UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的 usernameParameter 和 passwordParameter 两个参数的值进行修改。
-
(7)DefaultLoginPageGeneratingFilter:如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面。
-
(8)BasicAuthenticationFilter:检测和处理 http basic 认证。
-
(9)RequestCacheAwareFilter:用来处理请求的缓存。
-
(10)SecurityContextHolderAwareRequestFilter:主要是包装请求对象 request。
-
(11)AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在 Authentication 对象,如果不存在为其提供一个匿名 Authentication。
-
(12)SessionManagementFilter:管理 session 的过滤器
-
(13)ExceptionTranslationFilter:处理 AccessDeniedException 和
AuthenticationException 异常。 -
(14)AuthorizationFilter:可以看做过滤器链的出口。
-
(15)RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的 remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。