赞
踩
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
FilterChainProxy:其作为servlet请求中过滤链当中的一个,其本身也是一个过滤链,继承自GenericFilterBean。
SercurityFilterChain:其作为安全的过滤链,内部也是由一系列的Filter组成。
初始化链使用的Builder模式,当中使用Configurer来实现初始化及配置。Builer与Configurer的关系如下
SecurityBuilder:创建接口,方法为build。
AbstractSecurityBuilder:实现SecurityBuilder,实现了build方法,当中调用抽象方法doBuild,用于子类来实现具体的创建逻辑
AbstractConfiguredSecurityBuilder:继承自AbstractSecurityBuilder,实现了doBuild方法,提供了通过Configurer来初始化、配置SecurityBuilder,同时调用抽象performBuild方法执行构建,其由子类来实现。
WebSecurity:用于创建2.1中的FilterChainProxy
HttpSecurity:用于创建2.1中的SecurityChainFilter
WebSecurityConfigurerAdapter:作为WebSecurity的Configurer,用于添加HttpSecurity的Configurer
HttpSecurity**Configurer:主要是在org.springframework.security.config.annotation.web.configurers包下,用于配置HttpSecurity
主要是在WebSecurityConfiguration中创建。
WebSecurityConfigurer的获取,通过Spring容器中获取WebSecurityConfigurer类型的bean,并对其作排序,然后添加到WebSecurity中
- @Autowired(required = false)
- public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
- ConfigurableListableBeanFactory beanFactory) throws Exception {
- this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
- if (this.debugEnabled != null) {
- this.webSecurity.debug(this.debugEnabled);
- }
- List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new AutowiredWebSecurityConfigurersIgnoreParents(
- beanFactory).getWebSecurityConfigurers();
- webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
- Integer previousOrder = null;
- Object previousConfig = null;
- for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
- Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
- if (previousOrder != null && previousOrder.equals(order)) {
- throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order
- + " was already used on " + previousConfig + ", so it cannot be used on " + config + " too.");
- }
- previousOrder = order;
- previousConfig = config;
- }
- for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
- this.webSecurity.apply(webSecurityConfigurer);
- }
- this.webSecurityConfigurers = webSecurityConfigurers;
- }

也可以自定义SecurityFilterChain
- @Autowired(required = false)
- void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
- this.securityFilterChains = securityFilterChains;
- }
也支持提供了WebSecurityCustomizer对WebSecurity定制化
- @Autowired(required = false)
- void setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) {
- this.webSecurityCustomizers = webSecurityCustomizers;
- }
创建FilterChainProxy的bean
- @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
- public Filter springSecurityFilterChain() throws Exception {
- boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
- boolean hasFilterChain = !this.securityFilterChains.isEmpty();
- Assert.state(!(hasConfigurers && hasFilterChain),
- "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
- if (!hasConfigurers && !hasFilterChain) {
- WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
- .postProcess(new WebSecurityConfigurerAdapter() {
- });
- this.webSecurity.apply(adapter);
- }
- for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
- this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
- for (Filter filter : securityFilterChain.getFilters()) {
- if (filter instanceof FilterSecurityInterceptor) {
- this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
- break;
- }
- }
- }
- for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
- customizer.customize(this.webSecurity);
- }
- return this.webSecurity.build();
- }

创建逻辑如下
其组件图为
AbstractAuthenticationProcessingFilter:作为认证处理过滤器的基础框架。
AuthenticaionManager:是认证管理器接口,ProviderManager是其实现类,其依赖于AuthenticationProvider接口。
AbstractAuthenticationProcessingFilters的处理流程为
此层的Builder接口为ProviderManagerBuilder。Builder与Configurer的关系图为
UserDetailsAwareConfigurer:继承SecurityConfigurerAdapter,提供了获取UserDetailsService的抽象方法getUserDetailsService
AbstractDaoAuthenticationConfigurer:继承UserDetailsAwareConfigurer,实现getUserDetailsService抽象方法
DaoAuthenticationConfigurer:继承AbstractDaoAuthenticationConfigurer,其配置泛型参数为自身DaoAuthenticationConfigurer
UserDetailsServiceConfigurer:继承AbstractDaoAuthenticationConfigurer,其配置泛型参数为C extends UserDetailsServiceConfigurer<B, C, U>
UserDetailsManagerConfigurer:继承UserDetailsServiceConfigurer,其U extends UserDetailsService对应参数为UserServiceManager。
InMemoryUserDetailsManagerConfigurer:继承UserDetailsManagerConfigurer
JdbcUserDetailsManagerConfigurer:继承UserDetailsManagerConfigurer
LdapAuthenticationProviderConfigurer:继承SecurityBuilderAdapter
AuthenticationManager的创建是通过AuthenticationConfiguration来配置的。AuthenticationProvider是通过InitializeAuthenticationProviderBeanManagerConfigurer,InitializeUserDetailsManagerConfigurer来添加的,其中InitializeAuthenticationProviderBeanManagerConfigurer用来添加AuthenticationProvider的Bean,InitializeUserDetailsManagerConfigurer用来添加包含UserDetailsService的DaoAuthenticationProvider。这两个Configurer都是继承自GlobalAuthenticationConfigurerAdapter。
其层次关系为
创建AuthenticationManager是通过getAuthenticationManager方法来创建的
- public AuthenticationManager getAuthenticationManager() throws Exception {
- if (this.authenticationManagerInitialized) {
- return this.authenticationManager;
- }
- AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
- if (this.buildingAuthenticationManager.getAndSet(true)) {
- return new AuthenticationManagerDelegator(authBuilder);
- }
- for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
- authBuilder.apply(config);
- }
- this.authenticationManager = authBuilder.build();
- if (this.authenticationManager == null) {
- this.authenticationManager = getAuthenticationManagerBean();
- }
- this.authenticationManagerInitialized = true;
- return this.authenticationManager;
- }

授权时的会话管理是依赖SessionAuthenticationStrategy
RegisterSessionAuthenticationStrategy:负责在授权成功后,使用SessionRegistry注册管理用户
CsrfAuthenticationStrategy:用于删除旧的token,创建新的token
AbstractSessionFixationProtectionStrategy:处理会话固定攻击的基类
ChangeSessionIdAuthenticationStrategy:使用HttpServletRequest.changeSessionId修改sessionId来防止固定session攻击
SessionFixationProtectionStrategy:使用HttpServletRequest.invalidate来防止固定session攻击
CompositeSessionAuthenticationStrategy:组合一系列会话授权策略,内部维护了一个集合,保存多个不同的SessionAuthenticationStrategy。默认是(ConcurrentSessionControlAuthenticationStrategy,ChangeSessionIdAuthenticationStrategy, RegisterSessionAuthenticationStrategy)
ConcurrentSessionControlAuthenticationStrategy:处理session并发问题
NullAuthenticatedSessionStrategy:不做任何操作
SessionInformation用来记录会话信息。其定义为
- public class SessionInformation implements Serializable {
-
- private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
-
- private Date lastRequest;
-
- private final Object principal;
-
- private final String sessionId;
-
- private boolean expired = false;
-
- public SessionInformation(Object principal, String sessionId, Date lastRequest) {
- Assert.notNull(principal, "Principal required");
- Assert.hasText(sessionId, "SessionId required");
- Assert.notNull(lastRequest, "LastRequest required");
- this.principal = principal;
- this.sessionId = sessionId;
- this.lastRequest = lastRequest;
- }
-
- public void expireNow() {
- this.expired = true;
- }
-
- public Date getLastRequest() {
- return this.lastRequest;
- }
-
- public Object getPrincipal() {
- return this.principal;
- }
-
- public String getSessionId() {
- return this.sessionId;
- }
-
- public boolean isExpired() {
- return this.expired;
- }
-
- /**
- * Refreshes the internal lastRequest to the current date and time.
- */
- public void refreshLastRequest() {
- this.lastRequest = new Date();
- }
-
- }

会话注册是通过SessionRegistry接口管理。其实现类为SessionRegistryImpl,用来维护SessionInformation数据。
是在AbstractAuthenticationProcessingFilter的doFilter方法中
无效会话策略用于会话无效时的处理,是在SessionManagementFilter中使用。
用在ConcurrentSessionFilter中的会话信息过期的处理
AbstractUserDetailsAuthenticationProvider作为基于UserDetails的抽象基类,其提供了基础框架。
- public Authentication authenticate(Authentication authentication) throws AuthenticationException {
- String username = determineUsername(authentication);
- boolean cacheWasUsed = true;
- UserDetails user = this.userCache.getUserFromCache(username);
- if (user == null) {
- cacheWasUsed = false;
- try {
- user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
- }
- catch (UsernameNotFoundException ex) {
- if (!this.hideUserNotFoundExceptions) {
- throw ex;
- }
- throw new BadCredentialsException(this.messages
- .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
- }
- }
- try {
- this.preAuthenticationChecks.check(user);
- additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
- }
- catch (AuthenticationException ex) {
- if (!cacheWasUsed) {
- throw ex;
- }
- // There was a problem, so try again after checking
- // we're using latest data (i.e. not from the cache)
- cacheWasUsed = false;
- user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
- this.preAuthenticationChecks.check(user);
- additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
- }
- this.postAuthenticationChecks.check(user);
- if (!cacheWasUsed) {
- this.userCache.putUserInCache(user);
- }
- Object principalToReturn = user;
- if (this.forcePrincipalAsString) {
- principalToReturn = user.getUsername();
- }
- return createSuccessAuthentication(principalToReturn, authentication, user);
- }

密码通过PasswordEncoder加密后存储。其类层次图为
DelegatingPasswordEncoder:代理模式的实现类
LazyPasswordEncoder:懒加载的实现类,其在AuthenticationConfiguration中使用。其首先看ApplicationContext中是否有对应的bean,如果有则直接使用,没有就使用PasswordEncoderFactories来创建默认的密码加密器。
用于在会话间记住用户身份,主要是通过发送Cookie给浏览器,在后续的会话中检测到此Cookie则自动登录。
其类图为
AbstractRememberMeServices:作为RememberMeServices的抽象实现类,实现了接口,定义了处理的基本框架,暴露了抽象方法由子类来实现。其依赖于UserDetailsService接口
TokenBasedRememberMeServices:其基于hash的简单方法来处理。Cookie的生成方式为
- base64(urlencode(username)
- + ":"
- + urlencode(expiryTime)
- + ":"
- + urlencode(encodingAlgorithmName)
- + ":"
- +urlencode(encodingAlgorithm(username + ":" + tokenExpiryTime + ":" + password + ":" + key))
- )
PersistentTokenBasedRememberMeServices:其依赖PersistentTokenRepository接口用于token的持久化。支持两种持久化
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。