# 概述
- SpringSecurity是一个有效、高度定制化的认证与访问控制框架;
- SpringSecurity是一个致力于为java应用提供认证与授权能力的框架;
# 主体框架构成
- cas: 基于cas的客户端认证能力
- crypto: 加解密能力
- jwt: Json Web Tokens,客户端存储能力
- oauth2: oauth2三方授权及单点的能力
- web: 与web应用集成的能力
- ldap: Light Directory Access Protocol,轻量级目录访问协议,LDAP统一认证服务能力
- openid: 去中心化的数字身份识别系统认证能力
# 运行时序




# 主要官方过滤器说明
# 无条件过滤器
名称 | 作用 | 能否阻断 | 后置处理 |
---|---|---|---|
ChannelProcessingFilter | 确保通过所需的通道传递web请求 | 能 | 无 |
ConcurrentSessionFilter | 并发会话处理包所需的筛选器; 实现session的日期更新; 检查会话是否过期,是则删除,并广播事件; 进行登出操作; | 能 | 无 |
WebAsyncManagerIntegrationFilter | 提供SecurityContext与springWeb的WebAsyncManager之间的集成 | 不能 | 无 |
SecurityContextPersistenceFilter | 在请求之前,从配置的SecurityContextRepository中获取相应信息填充SecurityHolder; 请求完成后,清除holder并将其存储; | 不能 | 有 |
HeaderWriterFilter | 向当前响应添加响应头。可用于添加启用浏览器保护的某些响应头。像X-Frame-Options,X-XSS-Protection和X-Content-Type-Options。 | 不能 | 有 |
CorsFilter | 处理跨域的预检请求 | 能 | 无 |
CsrfFilter | 进行跨域请求伪造攻击防护 | 能 | 无 |
X509AuthenticationFilter | X509证书认证 | 能 | 无 |
RequestCacheAwareFilter | 如果当前请求被缓存过,则进行还原 | 不能 | 无 |
SecurityContextHolderAwareRequestFilter | 针对ServletRequest进行一次包装,使得request具有更加丰富的API | 不能 | 无 |
JaasApiIntegrationFilter | jaasApi集成过滤器 | 能 | 无 |
RememberMeAuthenticationFilter | 记住我过滤器 | 不能 | 无 |
AnonymousAuthenticationFilter | 匿名认证过滤器 | 不能 | 无 |
OAuth2AuthorizationCodeGrantFilter | OAuth2授权码授权过滤器 | 不能 | 无 |
SessionManagementFilter | 会话管理过滤器 | 能 | 无 |
ExceptionTranslationFilter | 异常转换过滤器 | 不能 | 有 |
FilterSecurityInterceptor | 过滤器拦截(授权)过滤器 | 能 | 无 |
SwitchUserFilter | 切换用户过滤器 | 能 | 无 |
# 条件过滤器
名称 | 作用 | 条件 | 阻断 | 后置 |
---|---|---|---|---|
LogoutFilter | 登出处理过滤器 | 路径匹配 | 是 | 无 |
OAuth2AuthorizationRequestRedirectFilter | 引导oauth2的授权码以及隐式授权码模式重定向到授权服务器的授权端点 | 路径匹配 | 是 | 有 |
CasAuthenticationFilter | Cas(服务端)登录的过滤器 | 路径匹配 | 是 | 是 |
OAuth2LoginAuthenticationFilter | OAuth2(服务端)登陆的过滤器 | 路径匹配 | 是 | 是 |
UsernamePasswordAuthenticationFilter | 表单登录认证过滤器 | 路径匹配 | 是 | 是 |
OpenIDAuthenticationFilter | openID认证过滤器 | 路径匹配 | 是 | 是 |
DefaultLoginPageGeneratingFilter | 默认登陆页生成过滤器 | 路径匹配 | 是 | 是 |
DefaultLogoutPageGeneratingFilter | 默认登出页生成过滤器 | 路径匹配 | 是 | 是 |
DigestAuthenticationFilter | http摘要认证过滤器 | 请求头匹配 | 是 | 是 |
BearerTokenAuthenticationFilter | OAuth2访问令牌认证过滤器 | 请求头匹配 | 是 | 是 |
BasicAuthenticationFilter | http基本令牌认证过滤器 | 请求头匹配 | 是 | 是 |
# SpringSecurityWeb全解析
# 与Spring环境的结合方式
/*xxx: EnableWebSecurity也是sping3开始便有的特性*/
public @interface EnableWebSecurity {
/*xxx: webSecurity可以打开配置说明,默认关闭*/
boolean debug() default false;
}
@Configuration
/*xxx: spring-security的默认配置项,可以自动配置*/
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
/*xxx: 默认情况下,采用该配置*/
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
/*xxx: 收集SecurityConfigurer,一般来说,单个SecurityConfigurer就代表一条过滤链*/
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
/*xxx: 省略其它抽象...*/
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
/*xxx: 这个bean的名称,也是有门道的,不能轻易变更*/
@Bean("springSecurityFilterChain")
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
/*xxx: 在没有配置的情况下,采用默认配置*/
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}
}
/*xxx: 该类位由spring-web项目提供实现*/
/*xxx: springSecurity与web集成时候的入口类*/
public class DelegatingFilterProxy extends GenericFilterBean {
/*xxx: 代理的filter类*/
private volatile Filter delegate;
public DelegatingFilterProxy(Filter delegate) {
Assert.notNull(delegate, "Delegate Filter must not be null");
this.delegate = delegate;
}
@Override
/*xxx: 作为一个过滤器 在 容器的 applicationFilterChain 进行过滤*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
/*xxx: 懒实例化过滤器代理对象 */
Filter delegateToUse = this.delegate;
/*xxx: 执行过滤器代理*/
invokeDelegate(delegateToUse, request, response, filterChain);
}
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
/*xxx: 通过过滤器代理进行过滤操作*/
delegate.doFilter(request, response, filterChain);
}
}
<filter>
<!--xxx: 需要注意,这个名字是固定的(在不更改默认源码架构的情况下)-->
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
/*xxx: web上下文初始化器,正常情况下,应该采用这个配置*/
public abstract class AbstractSecurityWebApplicationInitializer
implements WebApplicationInitializer {
public final void onStartup(ServletContext servletContext) throws ServletException {
insertSpringSecurityFilterChain(servletContext);
}
private void insertSpringSecurityFilterChain(ServletContext servletContext) {
String filterName = "springSecurityFilterChain";
DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(
filterName);
registerFilter(servletContext, true, filterName, springSecurityFilterChain);
}
private final void registerFilter(ServletContext servletContext,
boolean insertBeforeOtherFilters, String filterName, Filter filter) {
Dynamic registration = servletContext.addFilter(filterName, filter);
EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
/*xxx: 配置拦截模式*/
registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters,
"/*");
}
}
更多相关知识可参考: springMVC学习笔记-三个初始化器章节中WebApplicationInitializer部分内容
# 与SpringBoot环境的结合方式
@ConditionalOnClass({
AbstractSecurityWebApplicationInitializer.class,
SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
@Bean
/*xxx: 这个名字也至关重要 */
@ConditionalOnBean("springSecurityFilterChain")
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
"springSecurityFilterChain");
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
}
public class DelegatingFilterProxyRegistrationBean extends AbstractFilterRegistrationBean<DelegatingFilterProxy>
implements ApplicationContextAware {
@Override
public DelegatingFilterProxy getFilter() {
return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {
@Override
protected void initFilterBean() throws ServletException {
}
};
}
}
更多相关知识可参考:springMVC学习笔记-三个初始化器章节中ServletContextInitializer部分内容
# 过滤链组装--逻辑结构设计
public interface SecurityBuilder<O> {
/*xxx: 过滤链构造*/
O build() throws Exception;
}
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
/*xxx: 过滤链*/
private O object;
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
}
/*xxx: 执行构建*/
protected abstract O doBuild() throws Exception;
}
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder<O> {
/*xxx: 配置项注册表*/
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
init();
configure();
return performBuild();
}
}
/*xxx: 初始化*/
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
}
/*xxx: 配置*/
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
/*xxx: 执行构建*/
protected abstract O performBuild() throws Exception;
/*xxx: 收集配置*/
/*xxx: 注意,这里收集的 SecurityConfigurerAdapter ,可以是针对单个过滤链的配置,也可以增加过滤器链*/
/*xxx: 配置是添加过滤器链,还是配置过滤器,取决于配置项的 init行为*/
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
throws Exception {
configurer.setBuilder((B) this);
add(configurer);
return configurer;
}
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
/*xxx: 配置收集*/
synchronized (configurers) {
Class clazz = configurer.getClass();
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<SecurityConfigurer<O, B>>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
}
}
}
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
SecurityBuilder<Filter>, ApplicationContextAware {
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
@Override
protected Filter performBuild() throws Exception {
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
/*xxx: 构建过滤链*/
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
/*xxx: 构建过滤链*/
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
/*xxx: 构建过滤链代理*/
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
Filter result = filterChainProxy;
return result;
}
}
# 过滤配置器新建过滤链的情况
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http);
}
}
# 过滤配置器编辑过滤链的情况
public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<AnonymousConfigurer<H>, H> {
@Override
/*xxx: 注意参数类型的变化*/
public void init(H http) throws Exception {
if (authenticationProvider == null) {
authenticationProvider = new AnonymousAuthenticationProvider(getKey());
}
if (authenticationFilter == null) {
authenticationFilter = new AnonymousAuthenticationFilter(getKey(), principal,
authorities);
}
authenticationProvider = postProcess(authenticationProvider);
http.authenticationProvider(authenticationProvider);
}
}
# 过滤链编辑--逻辑结构设计
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
SecurityBuilder<DefaultSecurityFilterChain> {
/*xxx: 在过滤器之后加入过滤器*/
H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);
H addFilter(Filter filter);
/*xxx: 移除过滤器构造器*/
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(
Class<C> clazz);
/*xxx: 添加认证管理器 */
H authenticationProvider(AuthenticationProvider authenticationProvider);
/*xxx: 添加用户信息服务*/
H userDetailsService(UserDetailsService userDetailsService) throws Exception;
}
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
/*xxx: 提供了多个快捷的链式配置项,包括sessionManage,formLogin,rememberMe,cors等等*/
@Override
/*xxx: http默认为一条过滤链*/
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
}
# 过滤链构造器--逻辑结构设计
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
/*xxx: 各个配置器的初始化方法*/
void init(B builder) throws Exception;
/*xxx: 各个配置器统一调用的配置方法*/
void configure(B builder) throws Exception;
}
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
SecurityConfigurer<Filter, T> {
}
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
/*xxx: webSecurity的初始化钩子*/
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http);
}
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
http.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
/*xxx: 会话管理*/
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
}
}
/*xxx: 授权配置*/
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
/*xxx: 注意这个configure 与上面的configure的区别 */
/*xxx: 该函数是 过滤链构造时的一个钩子函数,详见: AbstractConfiguredSecurityBuilder的configure方法 */
/*xxx: 可用于添加 过滤器 */
@Override
public void configure(WebSecurity web) throws Exception {
}
}
# 过滤链流程设计
/*xxx: springSecuirty 用来负责 过滤器链的选择 ,springSecurity的实际入口 */
public class FilterChainProxy extends GenericFilterBean {
private List<SecurityFilterChain> filterChains;
public FilterChainProxy(List<SecurityFilterChain> filterChains) {
this.filterChains = filterChains;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
doFilterInternal(request, response, chain);
}
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall
.getFirewalledRequest((HttpServletRequest) request);
/*xxx: 从匹配的过滤链条中,获取过滤器列表 */
List<Filter> filters = getFilters(fwRequest);
/*xxx: 如果过滤器列表为空,则跳过 */
if (filters == null || filters.size() == 0) {
chain.doFilter(fwRequest, fwResponse);
return;
}
/*xxx: 否则,根据过滤器列表,构造虚拟过滤链*/
/*xxx: 则根据过滤器列表,创建一个虚拟过滤器链,并进行过滤 */
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
}
/*xxx: springSecurity中,最重要的方法,获取过滤链中的过滤器列表 */
private List<Filter> getFilters(HttpServletRequest request) {
/*xxx: 从所有的过滤链中,进行筛选,采用首次匹配原则*/
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
}
/*xxx: springSecuirty 用来负责 过滤器链的选择 ,springSecurity的实际入口 */
public class FilterChainProxy extends GenericFilterBean {
private static class VirtualFilterChain implements FilterChain {
private int currentPosition = 0;
/*xxx: 原生过滤链*/
private final FilterChain originalChain;
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (currentPosition == size) {
/*xxx: 当springSecurity的虚拟过滤链执行完毕后,需要再次执行 应用层面的过滤链*/
originalChain.doFilter(request, response);
}
else {
currentPosition++;
Filter nextFilter = additionalFilters.get(currentPosition - 1);
nextFilter.doFilter(request, response, this);
}
}
}
}
# 默认过滤链
@Configuration
/*xxx: spring-security的默认配置项,可以自动配置*/
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
@Bean(name = "springSecurityFilterChain")
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
/*xxx: 在没有配置的情况下,采用默认配置*/
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}
}
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
protected final HttpSecurity getHttp() throws Exception {
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
/*xxx: 会话管理*/
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
configure(http);
return http;
}
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
}
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
}
/*xxx: 过滤链*/
public interface SecurityFilterChain {
boolean matches(HttpServletRequest request);
List<Filter> getFilters();
}
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
/*xxx: 匹配器*/
private final RequestMatcher requestMatcher;
/*xxx: 过滤链中的过滤器集合*/
private final List<Filter> filters;
public List<Filter> getFilters() {
return filters;
}
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
}
# 认证--应用设计
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>
extends AbstractHttpConfigurer<T, B> {
private F authFilter;
protected AbstractAuthenticationFilterConfigurer(F authenticationFilter,
String defaultLoginProcessingUrl) {
this.authFilter = authenticationFilter;
if (defaultLoginProcessingUrl != null) {
loginProcessingUrl(defaultLoginProcessingUrl);
}
}
public T loginProcessingUrl(String loginProcessingUrl) {
this.loginProcessingUrl = loginProcessingUrl;
authFilter
.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
return getSelf();
}
protected abstract RequestMatcher createLoginProcessingUrlMatcher(
String loginProcessingUrl);
@Override
public void configure(B http) throws Exception {
authFilter.setAuthenticationManager(http
.getSharedObject(AuthenticationManager.class));
authFilter.setAuthenticationSuccessHandler(successHandler);
authFilter.setAuthenticationFailureHandler(failureHandler);
if (authenticationDetailsSource != null) {
authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
}
SessionAuthenticationStrategy sessionAuthenticationStrategy = http
.getSharedObject(SessionAuthenticationStrategy.class);
if (sessionAuthenticationStrategy != null) {
authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
}
RememberMeServices rememberMeServices = http
.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) {
authFilter.setRememberMeServices(rememberMeServices);
}
F filter = postProcess(authFilter);
http.addFilter(filter);
}
}
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return new AntPathRequestMatcher(loginProcessingUrl, "POST");
}
@Override
public void init(H http) throws Exception {
super.init(http);
initDefaultLoginFilter(http);
}
private void initDefaultLoginFilter(H http) {
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
.getSharedObject(DefaultLoginPageGeneratingFilter.class);
if (loginPageGeneratingFilter != null && !isCustomLoginPage()) {
/*xxx: 登录页地址*/
loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());
/*xxx: 认证地址*/
loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());
}
}
}
# 认证过滤器--逻辑设计
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
/*xxx: 认证管理器*/
private AuthenticationManager authenticationManager;
/*xxx:记住我的服务*/
private RememberMeServices rememberMeServices = new NullRememberMeServices();
/*xxx: session认证策略*/
private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
/*xxx: 结果处理器*/
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
/*xxx: 进行过滤的方法 */
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
/*xxx: 首先判断,是否符合当前的认证规则,不符合则放行*/
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
/*xxx: 获取认证信息 */
Authentication authResult = attemptAuthentication(request, response);
/*xxx: 进行session策略的认证 */
sessionStrategy.onAuthentication(authResult, request, response);
/*xxx: 调用认证成功处理器对结果进行处理*/
successfulAuthentication(request, response, chain, authResult);
}
/*xxx: 尝试进行认证*/
public abstract Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException,
ServletException;
}
/*xxx: 用户名密码过滤器,spring3.0开始设计*/
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
/*xxx: 尝试进行认证*/
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
/*xxx: 生成一个基本的 authentication 令牌*/
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
/*xxx: 为该 authentication 设置附加信息*/
setDetails(request, authRequest);
/*xxx: 调用 认证管理器,对令牌进行 认证*/
return this.getAuthenticationManager().authenticate(authRequest);
}
}
# 认证架构--逻辑设计
/*xxx: 一次完整的认证 可以包含多个 provider,由 providerManager进行管理*/
/*xxx: 认证管理器 */
public interface AuthenticationManager {
/*xxx: 迭代验证,直到有一个验证通过,则跳出*/
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
/*xxx: springSecurity 默认使用它来完成 迭代认证流程*/
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
/*xxx: 认证提供器集合*/
private List<AuthenticationProvider> providers = Collections.emptyList();
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Authentication result;
/*xxx: 获取所有的认证提供器,迭代对其进行认证 */
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
/*xxx: 如果当前的 认证提供器,支持该令牌的认证,则对其进行认证*/
result = provider.authenticate(authentication);
/*xxx: 认证成功后,需要将源Token 的详细信息 拷贝到 目标Token 中 */
/*xxx: 拷贝成功后,不再尝试后续的认证*/
if (result != null) {
copyDetails(authentication, result);
break;
}
}
return result;
}
}
public interface AuthenticationProvider {
/*xxx: 验证过程,成功则返回一个验证完成的 Authentication*/
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
/*xxx: 是否支持当前的Authentication类型*/
boolean supports(Class<?> authentication);
}
由此可以知道,在典型的http认证场景中,真正提供认证的不是AuthenticationManager,而是:AuthenticationProvider,它们是没有关系的两个类
与http认证不同的是,oauth2协议的资源服务器访问过程,则没有采用这一套体系,而是使用的原生OAuth2AuthenticationManager,继承自AuthenticationManager
# 认证主体流程--流程设计
public abstract class AbstractUserDetailsAuthenticationProvider implements
AuthenticationProvider, InitializingBean, MessageSourceAware {
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
/*xxx: 从令牌中,获取用户名*/
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
: authentication.getName();
/*xxx: 从缓存中,获取用户信息*/
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
/*xxx: 首先先检索用户 */
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
}
/*xxx: 检测用户账号是否可用: 包括是否锁定,是否冻结,是否过期*/
preAuthenticationChecks.check(user);
/*xxx: 附加认证检查,默认情况下,该方法就是检查 是否与给定的密码匹配*/
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
/*xxx: 检查用户密码是否过期 */
postAuthenticationChecks.check(user);
/*xxx: 返回认证通过的 Authentication */
return createSuccessAuthentication(principalToReturn, authentication, user);
}
/*xxx: 检索用户 */
protected abstract UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException;
}
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
/*xxx: 为认证提供数据来源*/
private UserDetailsService userDetailsService;
private UserDetailsPasswordService userDetailsPasswordService;
/*xxx: 检索用户*/
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
/*xxx: 根据用户名,获取用户信息,需要遵循 springSecurity的模型规则。 通过 UserService*/
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
return loadedUser;
}
}
public interface UserDetailsService {
/*xxx: 根据用户名查询用户*/
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
public class JdbcDaoImpl extends JdbcDaoSupport
implements UserDetailsService, MessageSourceAware {
/*xxx: 通过jdbc实现的数据源服务,实现略*/
}
# 认证令牌--逻辑设计
public abstract class AbstractAuthenticationToken implements Authentication,
CredentialsContainer {
private final Collection<GrantedAuthority> authorities;
private Object details;
private boolean authenticated = false;
public boolean isAuthenticated() {
return authenticated;
}
}
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
private final Object principal;
private Object credentials;
}
# 认证前置流程--从session中提取认证信息
public class SecurityContextPersistenceFilter extends GenericFilterBean {
static final String FILTER_APPLIED = "__spring_security_scpf_applied";
private SecurityContextRepository repo;
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
response);
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
/*xxx: 将恢复的认证信息,保存至当前线程上下文中 */
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());
}
}
public interface SecurityContextRepository {
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
}
public class HttpSessionSecurityContextRepository implements SecurityContextRepository {
public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
private String springSecurityContextKey = SPRING_SECURITY_CONTEXT_KEY;
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
HttpServletRequest request = requestResponseHolder.getRequest();
HttpServletResponse response = requestResponseHolder.getResponse();
HttpSession httpSession = request.getSession(false);
SecurityContext context = readSecurityContextFromSession(httpSession);
return contex;
}
private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
Object contextFromSession = httpSession.getAttribute(springSecurityContextKey);
return (SecurityContext) contextFromSession;
}
}
# 认证后置流程--认证信息保存至session
public class SecurityContextPersistenceFilter extends GenericFilterBean {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
try {
/*xxx: 省略其他抽象...*/
chain.doFilter(holder.getRequest(), holder.getResponse());
}finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
request.removeAttribute("__spring_security_scpf_applied");
}
}
}
public interface SecurityContextRepository {
void saveContext(SecurityContext context, HttpServletRequest request,
HttpServletResponse response);
}
public class HttpSessionSecurityContextRepository implements SecurityContextRepository {
public void saveContext(SecurityContext context, HttpServletRequest request,
HttpServletResponse response) {
SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils
.getNativeResponse(response,
SaveContextOnUpdateOrErrorResponseWrapper.class);
if (!responseWrapper.isContextSaved()) {
responseWrapper.saveContext(context);
}
}
}
public abstract class SaveContextOnUpdateOrErrorResponseWrapper
extends OnCommittedResponseWrapper {
protected abstract void saveContext(SecurityContext context);
}
final class SaveToSessionResponseWrapper extends
SaveContextOnUpdateOrErrorResponseWrapper {
@Override
protected void saveContext(SecurityContext context) {
final Authentication authentication = context.getAuthentication();
HttpSession httpSession = request.getSession(false);
/*xxx: 将上下文信息保存至session中*/
if (httpSession != null) {
httpSession.setAttribute("SPRING_SECURITY_CONTEXT", context);
}
}
}
# 认证后置流程--session控制
session与用户绑定
public interface SessionAuthenticationStrategy {
void onAuthentication(Authentication authentication, HttpServletRequest request,
HttpServletResponse response) throws SessionAuthenticationException;
}
public class RegisterSessionAuthenticationStrategy implements
SessionAuthenticationStrategy {
private final SessionRegistry sessionRegistry;
public void onAuthentication(Authentication authentication,
HttpServletRequest request, HttpServletResponse response) {
sessionRegistry.registerNewSession(request.getSession().getId(),
authentication.getPrincipal());
}
}
session并发控制
public interface SessionAuthenticationStrategy {
void onAuthentication(Authentication authentication, HttpServletRequest request,
HttpServletResponse response) throws SessionAuthenticationException;
}
/*xxx: 会话并发控制策略 */
public class ConcurrentSessionControlAuthenticationStrategy implements
MessageSourceAware, SessionAuthenticationStrategy {
/*xxx: 默认允许的并发数为1, 新的会话会踢掉旧的会话*/
private int maximumSessions = 1;
private final SessionRegistry sessionRegistry;
public ConcurrentSessionControlAuthenticationStrategy(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
public void onAuthentication(Authentication authentication,
HttpServletRequest request, HttpServletResponse response) {
final List<SessionInformation> sessions = sessionRegistry.getAllSessions(
authentication.getPrincipal(), false);
int sessionCount = sessions.size();
int allowedSessions = getMaximumSessionsForThisUser(authentication);
/*xxx: 允许的并发数大于当前并发数时,不做处理*/
if (sessionCount < allowedSessions) {
return;
}
/*xxx: -1表示不做限制*/
if (allowedSessions == -1) {
return;
}
/*xxx: 进行策略判断 */
allowableSessionsExceeded(sessions, allowedSessions, sessionRegistry);
}
protected void allowableSessionsExceeded(List<SessionInformation> sessions,
int allowableSessions, SessionRegistry registry)
throws SessionAuthenticationException {
sessions.sort(Comparator.comparing(SessionInformation::getLastRequest));
int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;
List<SessionInformation> sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);
/*xxx: 当新会话建立时,最早的会话过期*/
/*xxx: 注意,这一步只是标志session过期,但是需要该标志被检测的时候,才会进行剔除*/
for (SessionInformation session: sessionsToBeExpired) {
session.expireNow();
}
}
}
session并发管理的逻辑为:
- 用户的principal对应了多个 ids;
- 每个ids保存了session的信息;
- 以 UserDetails 为键,因此在覆写自定义的userDetails时,必须覆写 hashCode 和 equals方法
# session管理
- 在无特别设计的情况下,session的管理由servlet容器完成
- tomcat环境,session的管理由org.apache.catalina.session.StandardManager完成 更多知识详见:tomcat学习笔记--原生tomcat的session的生成逻辑
# 授权--逻辑结构设计
# 投票管理器
/*xxx: 访问决策管理器 */
public interface AccessDecisionManager {
/*xxx: 资源通行投票*/
void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
InsufficientAuthenticationException;
/*xxx: 是否能够参与投票*/
boolean supports(Class<?> clazz);
}
/*xxx: 抽象访问决策器*/
public abstract class AbstractAccessDecisionManager implements AccessDecisionManager,
InitializingBean, MessageSourceAware {
/*xxx: 投票器池*/
private List<AccessDecisionVoter<? extends Object>> decisionVoters;
public boolean supports(Class<?> clazz) {
for (AccessDecisionVoter voter : this.decisionVoters) {
if (!voter.supports(clazz)) {
return false;
}
}
return true;
}
}
/*xxx: 乐观投票器,只要有一个通过就算通过*/
public class AffirmativeBased extends AbstractAccessDecisionManager {
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
switch (result) {
/*xxx: 有一个通过,就算通过*/
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
}
}
/*xxx: 少数服从多数的投票器,如果通过的票数,多于反对的票数,则整体通过,反之同理*/
public class ConsensusBased extends AbstractAccessDecisionManager {
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int grant = 0;
int deny = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
/*xxx: 记录通过的票数,以及不通过的票数*/
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
grant++;
break;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (grant > deny) {
return;
}
if (deny > grant) {
/*xxx: 抛授权异常...*/
}
}
}
/*xxx: 悲观投票器,所有的投票器赞成,才算通过。有一个反对,则不通过*/
public class UnanimousBased extends AbstractAccessDecisionManager {
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes) throws AccessDeniedException {
int grant = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, singleAttributeList);
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
grant++;
break;
case AccessDecisionVoter.ACCESS_DENIED:
/*xxx: 投反对票,直接抛出资源访问异常*/
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
default:
break;
}
}
}
}
# 投票器
/*xxx: 访问策略投票器*/
public interface AccessDecisionVoter<S> {
int ACCESS_GRANTED = 1;
int ACCESS_ABSTAIN = 0;
int ACCESS_DENIED = -1;
boolean supports(Class<?> clazz);
int vote(Authentication authentication, S object,
Collection<ConfigAttribute> attributes);
}
public class RoleVoter implements AccessDecisionVoter<Object> {
private String rolePrefix = "ROLE_";
public boolean supports(Class<?> clazz) {
return true;
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null)
&& attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
}
else {
return false;
}
}
public int vote(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes) {
int result = ACCESS_ABSTAIN;
Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
for (ConfigAttribute attribute : attributes) {
if (this.supports(attribute)) {
result = ACCESS_DENIED;
for (GrantedAuthority authority : authorities) {
/*xxx: 判断是否有对应的角色信息*/
if (attribute.getAttribute().equals(authority.getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
}
# 授权--过程设计
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements
Filter {
private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";
/*xxx: 默认过滤链的最后一个过滤器*/
/*xxx: 抛出的异常,会被倒数第二个过滤器ExceptionTranslationFilter,进行处理*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
/*xxx: 将请求封装为 过滤器执行器*/
FilterInvocation fi = new FilterInvocation(request, response, chain);
/*xxx: 调用执行器*/
invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
/*xxx: 进行资源投票,如果授权未通过,则会抛错 */
/*xxx: 在这个阶段,也可能抛 认证异常,如果之前未进行认证*/
InterceptorStatusToken token = super.beforeInvocation(fi);
/*xxx: 对拦截器状态令牌,进行后置处理*/
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally {
super.finallyInvocation(token);
}
}
}
public class FilterInvocation {
private FilterChain chain;
private HttpServletRequest request;
private HttpServletResponse response;
}
public abstract class AbstractSecurityInterceptor implements InitializingBean,
ApplicationEventPublisherAware, MessageSourceAware {
/*xxx: 返回 拦截器状态令牌 */
protected InterceptorStatusToken beforeInvocation(Object object) {
/*xxx: 获取SecurityMedataSource元数据*/
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
/*xxx: 通过授权管理器,进行授权 */
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
}catch (AccessDeniedException accessDeniedException) {
throw accessDeniedException;
}
}
}
public interface SecurityMetadataSource extends AopInfrastructureBean {
Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException;
}
public class DefaultFilterInvocationSecurityMetadataSource implements
FilterInvocationSecurityMetadataSource {
private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;
public DefaultFilterInvocationSecurityMetadataSource(
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap) {
this.requestMap = requestMap;
}
public Collection<ConfigAttribute> getAttributes(Object object) {
final HttpServletRequest request = ((FilterInvocation) object).getRequest();
for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
.entrySet()) {
if (entry.getKey().matches(request)) {
return entry.getValue();
}
}
return null;
}
}
# 授权--元数据准备流程
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
protected void configure(HttpSecurity http) throws Exception {
http
/*xxx: 链式配置,根据配置拦截器的先后顺序,依次生效*/
.authorizeRequests().antMatchers("/test/**").hasAuthority("ROLE_TEST")
.anyRequest().authenticated()
.and()
/*xxx: 这个匹配拦截器,是针对过滤链的*/
/*xxx: 上方的匹配拦截器,是针对授权过程的*/
.antMatcher("/mySecurity/**");
}
}
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
/*xxx: 通过调用其 anyRequest(),antMatchers(),regexMatchers(),等方法来匹配系统的URL*/
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context))
.getRegistry();
}
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
C configurer) throws Exception {
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {
return existingConfig;
}
/*xxx: 收集配置,略*/
return apply(configurer);
}
}
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
extends
AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {
private final ExpressionInterceptUrlRegistry REGISTRY;
public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
}
}
public class ExpressionInterceptUrlRegistry
extends
AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl> {
}
abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R, T>, T>
extends AbstractConfigAttributeRequestMatcherRegistry<T> {
}
public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends
AbstractRequestMatcherRegistry<C> {
/*xxx: 通过url描述信息进行拦截*/
private List<UrlMapping> urlMappings = new ArrayList<>();
/*xxx: 未配置完成的映射匹配 */
private List<RequestMatcher> unmappedMatchers;
final void addMapping(UrlMapping urlMapping) {
/*xxx: 每次添加映射的时候,都会将未完成的映射匹配器置空,这非常重要*/
this.unmappedMatchers = null;
this.urlMappings.add(urlMapping);
}
/*xxx: 可以指定优先级的方式指定拦截器*/
/*xxx: PermitAllSupport.permitAll()方法有调用*/
final void addMapping(int index, UrlMapping urlMapping) {
this.urlMappings.add(index, urlMapping);
}
final LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> createRequestMap() {
if (unmappedMatchers != null) {
/*xxx: 抛错...*/
}
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
for (UrlMapping mapping : getUrlMappings()) {
RequestMatcher matcher = mapping.getRequestMatcher();
Collection<ConfigAttribute> configAttrs = mapping.getConfigAttrs();
requestMap.put(matcher, configAttrs);
}
return requestMap;
}
}
static final class UrlMapping {
private RequestMatcher requestMatcher;
private Collection<ConfigAttribute> configAttrs;
}
public abstract class AbstractRequestMatcherRegistry<C> {
private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
public C anyRequest() {
return requestMatchers(ANY_REQUEST);
}
public C requestMatchers(RequestMatcher... requestMatchers) {
return chainRequestMatchers(Arrays.asList(requestMatchers));
}
public C antMatchers(String... antPatterns) {
return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
}
protected abstract C chainRequestMatchers(List<RequestMatcher> requestMatchers);
}
# 授权元数据-逻辑设计
public interface ConfigAttribute extends Serializable {
String getAttribute();
}
public class SecurityConfig implements ConfigAttribute {
private final String attrib;
@Override
public String getAttribute() {
return this.attrib;
}
}
class WebExpressionConfigAttribute implements ConfigAttribute,
EvaluationContextPostProcessor<FilterInvocation> {
private final Expression authorizeExpression;
@Override
public String getAttribute() {
return null;
}
@Override
/*xxx: 通过这个方法进行辅助投票的*/
public EvaluationContext postProcess(EvaluationContext context, FilterInvocation fi) {
return this.postProcessor == null ? context
: this.postProcessor.postProcess(context, fi);
}
}
# 关键过滤器流程设计
# 异常转换器流程设计
public class ExceptionTranslationFilter extends GenericFilterBean {
/*xxx: 默认情况下,这个异常转换的过滤器,位于默认过滤器链的倒数第二个位置,用于处理 授权失败的情况*/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
chain.doFilter(request, response);
}catch (Exception ex) {
/*xxx: 提取异常类型*/
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
RuntimeException ase = (AuthenticationException) throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (ase == null) {
ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(
AccessDeniedException.class, causeChain);
}
if (ase != null) {
/*xxx: 对springSecurity的异常进行处理 */
handleSpringSecurityException(request, response, chain, ase);
}else{
/*xxx: 抛出其它原生错误... 略*/
}
}
}
/*xxx: 对于出现的错误,有两种处理方式,分别对应了 认证错误(认证错误包含了 匿名认证 的情况),以及 授权错误*/
private void handleSpringSecurityException(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, RuntimeException exception)
throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
/*xxx: 第一种,通过认证端口,处理异常 */
sendStartAuthentication(request, response, chain,
(AuthenticationException) exception);
}else if (exception instanceof AccessDeniedException) {
if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {
/*xxx: 第一种,通过认证端口,处理异常(当前未匿名认证时,采用该方式) */
sendStartAuthentication(request, response, chain,
new InsufficientAuthenticationException("..."));
}else{
/*xxx: 第二种,通过 AccessDeniedHandler进行处理(内部)*/
accessDeniedHandler.handle(request, response,
(AccessDeniedException) exception);
}
}
}
protected void sendStartAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
AuthenticationException reason) throws ServletException, IOException {
/*xxx: 该方法,如果之前没有会话信息(即之前的会话请求中,没有sessionId),则会维持会话信息 */
/*xxx: 通俗说,就是会创建session*/
requestCache.saveRequest(request, response);
/*xxx: cas客户端登录,貌似就是用的这种方式*/
authenticationEntryPoint.commence(request, response, reason);
}
}
/*xxx: ExceptionTranslationFilter 用于启动身份验证方案*/
/*xxx: 认证入口点 */
public interface AuthenticationEntryPoint {
/*xxx: 开始认证*/
void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException;
}
/*xxx: 进行登录,支持两种策略: forward与redirect*/
public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoint,
InitializingBean {
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
String redirectUrl = null;
if (useForward) {
redirectUrl = buildHttpsRedirectUrlForRequest(request);
RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);
dispatcher.forward(request, response);
return;
}else {
redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);
redirectStrategy.sendRedirect(request, response, redirectUrl);
}
}
}
/*xxx: 内部处理 accessDenied的策略*/
public interface AccessDeniedHandler {
void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException,
ServletException;
}
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException,
ServletException {
if (!response.isCommitted()) {
/*xxx: 有错误页的情况下,转发到错误页面*/
if (errorPage != null) {
response.setStatus(HttpStatus.FORBIDDEN.value());
RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
dispatcher.forward(request, response);
}else {
response.sendError(HttpStatus.FORBIDDEN.value(),
HttpStatus.FORBIDDEN.getReasonPhrase());
}
}
}
}
# session控制过滤器
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception {
return getOrApply(new SessionManagementConfigurer<>());
}
}
public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<SessionManagementConfigurer<H>, H> {
@Override
public void configure(H http) throws Exception {
SecurityContextRepository securityContextRepository = http
.getSharedObject(SecurityContextRepository.class);
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(
securityContextRepository, getSessionAuthenticationStrategy(http));
http.addFilter(sessionManagementFilter);
if (isConcurrentSessionControlEnabled()) {
ConcurrentSessionFilter concurrentSessionFilter = createConccurencyFilter(http);
concurrentSessionFilter = postProcess(concurrentSessionFilter);
http.addFilter(concurrentSessionFilter);
}
}
private boolean isConcurrentSessionControlEnabled() {
return this.maximumSessions != null;
}
}
public class SessionManagementFilter extends GenericFilterBean {
private final SecurityContextRepository securityContextRepository;
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
/*xxx: 认证上下文没有当前请求信息,才会进行处理*/
if (!securityContextRepository.containsContext(request)) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (authentication != null && !trustResolver.isAnonymous(authentication)) {
sessionAuthenticationStrategy.onAuthentication(authentication,
request, response);
securityContextRepository.saveContext(SecurityContextHolder.getContext(),
request, response);
}else{
if (request.getRequestedSessionId() != null
&& !request.isRequestedSessionIdValid()) {
invalidSessionStrategy
.onInvalidSessionDetected(request, response);
}
}
}
chain.doFilter(request, response);
}
}
public class ConcurrentSessionFilter extends GenericFilterBean {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
/*xxx: 剔除本身session*/
if (session != null) {
SessionInformation info = sessionRegistry.getSessionInformation(session
.getId());
if (info != null) {
if (info.isExpired()) {
doLogout(request, response);
}else{
sessionRegistry.refreshLastRequest(info.getSessionId());
}
}
}
chain.doFilter(request, response);
}
}
# 过滤器对流程控制的影响
- 过滤器可以在内部终止流程;
- 过滤器可以编排成链,从而完成指定的功能;
- 过滤链的执行逻辑结构为树结构的层级遍历,流程既可以在叶子节点终结,也可以在根节点终结;
# 项目关键要素
# 默认情况下,登录密码的校验
- 校验过程
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private PasswordEncoder passwordEncoder;
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
} else {
String presentedPassword = authentication.getCredentials().toString();
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
}
}
- 密码校验器的设置过程
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
ObjectPostProcessor<Object> objectPostProcessor = (ObjectPostProcessor)context.getBean(ObjectPostProcessor.class);
WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(context);
//xxx:从上下文中注入passwordEncoder,并以该encoder作为后续组件的组成部分,构建执行逻辑
this.authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
}
}