Spring Security
Tips
http://www.baeldung.com/spring-security-login
Notes
@EnableGlobalMethodSecurity
- 标识被注解的类可以用于配置
AuthenticationManagerBuilder
, 即可以注入该配置对象 - 该注解可以作为元注解使用, 因此
@EnableWebSecurity
和@EnableWebMvcSecurity
都有这样的功能 AuthenticationConfiguration
- 标识被注解的类可以用于配置
@EnableWebSecurity
- 如果有 mvc 环境, 会启用
WebMvcSecurityConfiguration
配置 WebMvcSecurityConfiguration
- 添加了
@AuthenticationPrincipal
方法参数注解的处理 - 添加了
CsrfToken
参数注入
- 添加了
WebSecurityConfiguration
- 使用
WebSecurity
来构建一个Filter
(springSecurityFilterChain) WebSecurity
- 核心配置上下文
WebSecurityConfigurer
- 从
AutowiredWebSecurityConfigurersIgnoreParents
取得 - 继承自
SecurityConfigurer
- 配置结果为
Filter
- 未添加任何其他的方法
- 配置结果为
- 从
- 使用
- 如果有 mvc 环境, 会启用
@EnableWebMvcSecurity
- 已弃用, 使用
@EnableWebSecurity
- 已弃用, 使用
@EnableGlobalMethodSecurity
GlobalMethodSecuritySelector
- 根据注解上启用的功能来导入配置
GlobalMethodSecurityConfiguration
MethodInterceptor
- 使用配置项构建的方法拦截 (methodSecurityInterceptor)
AccessDecisionManager
- 默认为
AffirmativeBased
PreInvocationAuthorizationAdviceVoter
- 实现
@PreFilter
和@PreAuthorize
- 实际上该类型的判断不需要作为基于投票的判断, 因为已经能从信息中拿到结果, 不过为了可读性才以基于投票的系统来实现
- 实现
RoleVoter
AuthenticatedVoter
- 默认为
AfterInvocationManager
- 如果没有开启前后处理(prePost), 那么不会有该实现
- 该接口主要用于实现后处理
- 默认为
AfterInvocationProviderManager
- 实际决策由
AfterInvocationProvider
处理 PostInvocationAdviceProvider
PostInvocationAuthorizationAdvice
ExpressionBasedPostInvocationAdvice
PostInvocationExpressionAttribute
- 实现
@PostFilter
和@PostAuthorize
的处理
- 实际决策由
MethodSecurityMetadataSource
- 一个
SecurityMetadataSource
的实现 - 用于生成不同访问决策
AccessDecisionManager
需要的ConfigAttribute
- 一个
RunAsManager
- 处理
@RunAs
- 默认没有实现
- 执行时创建一个新的授权对象
- 处理
Jsr250MetadataSourceConfiguration
@DenyAll
,@PermitAll
,@RolesAllowed
SecurityConfigurer
- 基础的安全配置配置接口
- 核心的配置配置内容均继承自该接口
- 分为配置结果和配置上下文
SecurityBuilder
- 配置上下文的基础接口
- 最终由该接口提供的方法配置出结果配置对象
- 主要实现
WebSecurity
HttpSecurityBuilder
HttpSecurity
- 结果类型为
DefaultSecurityFilterChain
- 默认的 Filter 链顺序
ChannelProcessingFilter
- 主要用于判断是否是从
https
端口来的请求 - 构建
FilterInvocation
- 拿到相应的安全配置属性
ConfigAttribute
- 最终交由
ChannelDecisionManager
判断
- 主要用于判断是否是从
ConcurrentSessionFilter
- 默认的 Filter 链中有三个
ConcurrentSessionFilter
- 主要用于处理并发会话
- 主要做两件事
- 调用
SessionRegistry#refreshLastRequest(String)
- 保证会 话的上次更新时间是正确的
- 从
SessionRegistry
拿SessionInformation
, 并判断是否过期
- 如果过期了则执行退出登录逻辑
- 调用
- 默认的 Filter 链中有三个
SecurityContextPersistenceFilter
- 将从
SecurityContextRepository
拿到的安全上下文SecurityContext
放到SecurityContextHolder
- 必须在所有授权机制之前执行
- 使用请求对象的属性(
__spring_security_scpf_applied
) 来保证只执行一次 SecurityContextRepository
- 用于在请求之间持久化
SecurityContext
信息的接口 - 大多是情况下是使用
HttpSession
来存储 - 加载时不会反回
null
, 如果没有, 会返回一个空的上下文 HttpSessionSecurityContextRepository
- 会话存储键
SPRING_SECURITY_CONTEXT
- 会话存储键
NullSecurityContextRepository
- 如果不启用会话则会使用该实现
- 加载上下文时会使用
SecurityContextHolder#createEmptyContext
创建空的上下文
- 用于在请求之间持久化
- 将从
LogoutFilter
- 处理匹配(
RequestMatcher
)的退出登录请求 - 实际退出登录操作交由
LogoutHandler
处理 - 退出登录成功后通知
LogoutSuccessHandler
- 主要实现
CookieClearingLogoutHandler
- 清理 Cookie
CsrfLogoutHandler
- 清理 csrf
SecurityContextLogoutHandler
- 失效会话
- 去除安全上下文中的授权
AbstractRememberMeServices
- 清除 RememberMe 信息
- 处理匹配(
X509AuthenticationFilter
AbstractPreAuthenticatedProcessingFilter
- 所有进行预认证的处理
- 主要处理由外部系统认证的情况
- 从请求中提取出
principal
, 而不是对他们进行认证 - 当一个认证失败后会尝试其他的认证机制
continueFilterChainOnUnsuccessfulAuthentication
- 如果已经进行过授权则不会再次进行
- 主要实现
X509AuthenticationFilter
- 最先执行
RequestHeaderAuthenticationFilter
- CA SSO
- CA Siteminder
SM_USER
头
RequestAttributeAuthenticationFilter
- Stanford WebAuth
- Shibboleth
- Shibboleth (Internet2)
REMOTE_USER
请求属性
CasAuthenticationFilter
- 处理 cas 认证
UsernamePasswordAuthenticationFilter
- 由
formLogin
配置 - 取表单中的用户名密码进行认证
- 由
ConcurrentSessionFilter
OpenIDAuthenticationFilter
DefaultLoginPageGeneratingFilter
- 生成默认登录页
- 默认地址为
/login
ConcurrentSessionFilter
DigestAuthenticationFilter
- 处理HTTP 摘要认证
- Digest 的弱点
- 要求密码可恢复
BasicAuthenticationFilter
- 处理 HTTP 基本验证
RequestCacheAwareFilter
- 从
RequestCache
恢复缓存的请求 - 允许重定向到认证机制后重新开始单个请求
- 从
SecurityContextHolderAwareRequestFilter
- 包装当前的请求, 以实现 Servlet 3 请求上安全相关的方法
HttpServletRequest#authenticate(HttpServletResponse)
HttpServletRequest#login(String, String)
HttpServletRequest#logout()
AsyncContext#start(Runnable)
- 包装当前的请求, 以实现 Servlet 3 请求上安全相关的方法
JaasApiIntegrationFilter
- JAAS 集成
RememberMeAuthenticationFilter
- 如果未找到当前的认证信息, 则尝试从
RememberMeServices
中构建认证
- 如果未找到当前的认证信息, 则尝试从
AnonymousAuthenticationFilter
- 如果没有认证信息, 则构建一个匿名认证信息
SessionManagementFilter
- 根据当前的认证信息执行会话相关的策略
ExceptionTranslationFilter
- 处理抛出的
AccessDeniedException
和AuthenticationException
异常 - 将异常信息转换为对应的 HTTP 响应
AuthenticationException
- 会尝试返回请求从新要求认证
AccessDeniedException
- 如果当前认证是匿名的或者是记住我, 则尝试从新开始认证
- 否则交由
AccessDeniedHandler
处理
- 处理抛出的
FilterSecurityInterceptor
- 执行
Filter
层的拦截处理
- 执行
SwitchUserFilter
- 处理用户切换请求
/login/impersonate
- 处理退出切换的用户
/logout/impersonate
- 例如: 将用户从
ROLE_ADMIN
切换为ROLE_USER
- 处理用户切换请求
- 主要实现
SecurityConfigurerAdapter
- 添加了对象后处理配置
ObjectPostProcessor
- 添加了
SecurityBuilder
链式调用的处理
- 添加了对象后处理配置
WebSecurityConfigurerAdapter
- Builder 类型为
WebSecurity
ContentNegotiationStrategy
- 配置内容协商决策
- 请求对象 -> 媒体类型
- 授权配置
- 授权管理器配置
- Http 安全配置
- 默认配置
- csrf
- 异步集成
- 异常处理
- 头处理
- 会话管理
- 安全上下文
- 请求缓存
- 匿名请求
- servlet 接口
- 默认登录页
- 退出登录
- 默认配置
- Builder 类型为
AbstractHttpConfigurer
- Builder 类型为
HttpSecurity
- 配置结果为
DefaultSecurityFilterChain
- Builder 类型为
AuthenticationEventPublisher
- 用于发布授权成功或失败的 Publisher
AuthenticationSuccessEvent
- 失败的授权会根据异常类型不同映射为不同的事件
- 用于发布授权成功或失败的 Publisher
UserDetailsService
- 安全系统中的用户概念
- 该接口提供只读功能
UserDetails
- 用户信息
UserDetailsManager
- 提供对用户信息的管理
GroupManager
- 用户分组管理
AuthenticationUserDetailsService
- 从认证中拿到用户信息
AuthenticationTrustResolver
- 判断一个认证是否为匿名或记住我
Doc Notes
- Secure Object Implementations
- PermissionEvaluator 用于处理 hasPermission
- 方法安全的 AOP 主要基于 MethodSecurityInterceptor
- 方法安全相关的注解
- PreAuthorize
- PostAuthorize
- PreFilter
- PostFilter
- 可以使用 AuthenticationPrincipal 注入上下文中的授权对象 Authentication
- 如果使用了代理服务器, 需要配置正确的 ForwardedHeaderFilter
- 可以使用 RoleHierarchy 来实现角色间的层级关系
- 如果不是使用的传统的授权方式, 可以考虑添加自己的 AuthenticationEntryPoint 到 http 上
- 执行安全相关的主体逻辑在 AbstractSecurityInterceptor 中
- AbstractSecurityInterceptor
- 授权验证
- 前后验证
- 访问决策
- RunAs
- 会替换当前请求上下文中的
Authentication
- 会替换当前请求上下文中的
- 分别分为 MethodInvocation 和 FilterInvocation
- MethodInvocation 对 AOP 对方法调用的拦截
- FilterInvocation 对 Servlet 请求响应的拦截
- 会根据 Request 找到响应的处理
- 例如:
.antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
- 会生成一个 RequestMatcher 实现为 AntPathRequestMatcher
- RequestMatcher 可能会从 request 中抽取出变 量
- 会生成 SecurityConfig , 会由 MetadataSource 转换为 WebExpressionConfigAttribute
- 会由 WebExpressionVoter 处理
- 是 AccessDecisionVoter 的实现
- 基于投票的决策
- 投票结果为 ACCESS_GRANTED, ACCESS_ABSTAIN, ACCESS_DENIED
- 可能会由以下 AccessDecisionManager 实现处理
- AffirmativeBased
- 任意
- ConsensusBased
- 大多数, 不包括放弃投票的
- UnanimousBased
- 所有都投同意或放弃
- 是 AccessDecisionVoter 的实现
- 最终会汇聚到 ExpressionBasedFilterInvocationSecurityMetadataSource
- 会生成一个 RequestMatcher 实现为 AntPathRequestMatcher
- 例如:
- 会根据 Request 找到响应的处理
- 角色之所以会使用
ROLE_
前缀, 是因为RoleVoter
会将所有有该前缀的认为是橘色, 并作出判断 - ConfigAttribute
- What are Configuration Attributes
- 是将所有权限验证串起来的关键
- 每个验证都会生成相应的属性, 每个属性有各自的类型, 每个类型的属性也对应了相应的决策判断
- 由 SecurityMetadataSource 创建, 主要针对 AOP, 分为方法调用和 Filter 调用
- 由 AccessDecisionManager 处理, 处理之前都会判断相应的属性类型
- AspectJSecurityInterceptor 主要用于 DomainObject 级别的 ACL
- ExceptionTranslationFilter 会处理与授权相关的异常
- AuthenticationException
- AccessDeniedException
- 如果是授权失败 AuthenticationException
- 先 RequestCache#savedRequest
- 例如用户授权成功后跳转
- 会使用 AuthenticationEntryPoint 来解决异常
- 先 RequestCache#savedRequest
- 如果是拒绝访问 AccessDeniedException
- 如果是发现由于是因为未完全授 权(匿名或记住我),也会触发上面授权失败的处理
- 会使用 AccessDeniedHandler 来解决异常
- 对于 RESTful 接口服务来说, 该 Filter 实际上是不需要的
- AuthenticationTrustResolver 可以用于实现对匿名和记住我的判断
- ThrowableAnalyzer 可以用来辅助异常处理
- AbstractSecurityWebSocketMessageBrokerConfigurer 可对 WebSocket 进行安全处理
- Authentication vs Authorization
- Authentication
- Who you are
- Login
- HTTP 401 Unauthorized
- Authorization
- What you are allowed to do
- Permission
- HTTP 403 Forbidden
- Authentication
- Spring Data 集成
- 需要添加依赖
spring-security-data
- 自动配置
org.springframework.boot.autoconfigure.security.SecurityDataConfiguration
- 初始化
org.springframework.security.data.repository.query.SecurityEvaluationContextExtension
- 需要添加依赖
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
@Query
可以使用 Spring Security 的表达式@Query("select m from Message m where m.to.id = ?#{ principal?.id }")
- 并发
- 由于授权是线程相关的, 如果要并发使用可以使用框 架提供的包装类
- DelegatingSecurityContextRunnable
- DelegatingSecurityContextExecutor
- 由于授权是线程相关的, 如果要并发使用可以使用框 架提供的包装类
// 基于表达式的访问控制
// 即便不是用 access, 实际生成的也都是类似的表达式
http
.authorizeRequests()
// 可以直接调用上下文中的 bean 对象来检测
// 其中的 request 参数是在 WebSecurityExpressionRoot 定义的
.antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
// 访问路径参数
.antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)")
ACL
- Domain Object Security
- spring-security-acl
- 主要用于控制对象权限
- 使用
ACL_
数据表- ACL_SID
- SID -> Security Identifier
- 系统中 principal 或 authority 的唯一标识符
- ACL_CLASS
- 系统中 DO 的唯一标识符
- ACL_OBJECT_IDENTITY
- DO 实例的唯一标识符
- ACL_ENTRY
- 存储指派给 recipient 的单独权限
- 包含 ACL_OBJECT_IDENTITY, ACL_SID 和权限的 bit mask
- ACL_SID
- 主要接口
- Acl
- 每个 DO 对应一个 Acl, 包含
AccessControlEntry
信息, 不直接指向 DO, 但包含ObjectIdentity
. - 存储于
ACL_OBJECT_IDENTITY
表
- 每个 DO 对应一个 Acl, 包含
- AccessControlEntry
- 包含多个
AccessControlEntry
- 包含多个
- Acl