这篇指南是对Spring Security的一个入门简介,为我们提供了一个对框架底层设计的深入了解。
这里仅仅涵盖了该框架非常基础的的知识,尽管如此,本文也可以解决开发者在使用本框架的过程中的一些疑惑。
在这里,我们会看看在web应用程序中我们是如何使用filter以及更通用的方法注解两种途径将Spring Security框架应用到我们的项目中的。
当你希望从一个较高水平理解该安全框架是如何工作,如何定制化开发,或者你仅仅希望学习如何看待Spring Security框架的时候,就可以来看看本指南。
本指南不是一个解决基本问题的手册或者菜谱(有其他的资源可以实现这些),但是这对初学者和专家都会有一些帮助。
认证与访问控制
应用程序的安全归结为两点,认证(你是谁?),授权(你可以做什么)。有时候,授权也被称为访问控制,这会让人感到迷惑。
在Spring Security的架构中,框架将认证和授权分开设计,并且为二者提供了策略和扩展点。
认证
主要的认证策略接口是AuthenticationManager
,该接口仅仅有一个方法。
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
在该方法中, AuthenticationManager
可以做下面三种事之一:
1. 当输入的认证信息是有效的时候会返回一个Authentication
(其authenticated=true)。
2. 当输入的认证信息是无效的时候,抛出一个AuthenticationException
异常。
3. 当它无法确定的时候,返回null。
AuthenticationException
异常是一个运行时异常,程序通常会捕获该异常,并输出异常信息。
ProviderManager是AuthenticationManager
接口最常见的实现。而该类又将其职责委派给AuthenticationProvider
实例链。
一个AuthenticationProvider和AuthenticationManager
有一些像,但是它又有额外的方法方便调用者确定其是否支持某一个给定的Authentication类型。
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
在同一个程序中,ProviderManager
可以通过委派一个AuthenticationProviders实例链,支持多个不同的认证机制。如果ProviderManager没有委托任何的
Authentication实例类型,那么认证将被跳过。
一个ProviderManager有一个可选的祖先,在所有的provider返回null的时候,程序可以咨询该祖先,如果祖先不可用,那么程序会抛出
AuthenticationException异常。
有时候,一个应用程序有一个逻辑上的被保护资源集合。(比如说/api/**),每一个集合可以拥有自己专用的AuthenticationManager。
定制Authentication Managers
Spring Security提供了一些配置助手来快速地在我们的应用程序中建立起通用的authentication manager特性。最常用的助手是AuthenticationManagerBuilder,可以被用来创建基于内存,数据库或者ldap的用户细节,或者添加一个定制化的UserDetailsService。下面是一个应用程序中的全局AuthenticationManager示例:
@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
... // web stuff here
@Autowired
public initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
.password("secret").roles("USER");
}
}
授权/访问控制
认证成功之后,我们就可以开始进行授权了。这里的核心策略是AccessDecisionManager接口。框架提供了三种实现,并且三种实现都委派给了AccessDecisionVoter链,有一点像ProviderManager委派给AuthenticationProviders。
boolean supports(ConfigAttribute attribute);
boolean supports(Class<?> clazz);
int vote(Authentication authentication, S object,
Collection<ConfigAttribute> attributes);
一个AccessDecisionVoter
牵扯到了Authentication
和一个安全Object。
Object在AccessDecisionManager和AccessDecisionVoter
的签名中是通用的。代表了任何一个用户想要访问的事务(资源或者java类中的方法)。
ConfigAttributes实例也是通用的,代表了对Object实例的一些元数据装饰,这些元数据确定了一个对象可以被访问的权限级别。
ConfigAttribute是一个只有一个返回String实例的方法的接口,这些字符串按照自愿拥有者的意愿进行编码,表示了谁可以访问该资源的规则。
一个典型的ConfigAttribute是一个用户角色名称(
ROLE_ADMIN或者ROLE_AUDIT)。
Web Security
Spring Security框架在web层依赖于Servlet的Filters。因此,我们先来看看Filters的通用角色。Filter通常位于Client和Servlet之间。
Spring Security是安装在Filter链中一个Filter,其实际类型是FilterChainProxy。其在Filter中的顺序由SecurityProperties.DEFAULT_FILTER_ORDER定义,