承接前文springboot情操陶冶-web配置(八),本文在前文的基础上深入了解下WebSecurity类的运作逻辑

WebSecurityConfigurerAdapter

在剖析WebSecurity的工作逻辑之前,先预热下springboot security推荐复写的抽象类WebSecurityConfigurerAdapter,观察下其是如何被实例化的,方便后续的深入理解


1.ApplicationContext环境设置

	@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context; ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
//
authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
@Override
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
authenticationBuilder.eraseCredentials(eraseCredentials);
return super.eraseCredentials(eraseCredentials);
} };
}

同前文的认证管理器创建差不多,但这里细心的可以看到其内部创建了两个一模一样的认证管理器对象,目的应该是为了引用AuthenticationConfiguration对象中的认证管理器作准备,下文会提及


2.引入前文所提的AuthenticationConfiguration对象

	@Autowired
public void setAuthenticationConfiguration(
AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}

目的是间接调用此类获取对应的认证管理器AuthenticationManager对象,具体的读者可自行阅读


3.共享变量集合,security板块引入了共享变量,目的就跟缓存一样

	private Map<Class<? extends Object>, Object> createSharedObjects() {
Map<Class<? extends Object>, Object> sharedObjects = new HashMap<Class<? extends Object>, Object>();
sharedObjects.putAll(localConfigureAuthenticationBldr.getSharedObjects());
sharedObjects.put(UserDetailsService.class, userDetailsService());
sharedObjects.put(ApplicationContext.class, context);
sharedObjects.put(ContentNegotiationStrategy.class, contentNegotiationStrategy);
sharedObjects.put(AuthenticationTrustResolver.class, trustResolver);
return sharedObjects;
}

OK,差不多就这样,开始我们的主角之旅把

WebSecurityConfiguration

根据前文可知,WebSecurity对象的创建与运作都是通过WebSecurityConfiguration来的,笔者在此处再作下简单的归纳

1.实例化WebSecurity对象并注册至ApplicationContext上下文对象中

2.获取ApplicationContext对象上注册的类型为WebSecurityConfigurer的接口集合,存放至WebSecurity的父类AbstractConfiguredSecurityBuilder的内部属性configurers(hashmap)

3.运行WebSecurity的build()方法创建Filter过滤链

基于上述的结论我们再着重看下第三点的创建过滤链是如何完成的,本文侧重点也在于此

WebSecurity#build()

build()方法是由其父类AbstractSecurityBuilder来完成的,代码很简单

	public final O build() throws Exception {
// 确保只会被build一次
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}

接着跟踪doBuild()模板方法,发现是由其子类抽象类AbstractConfiguredSecurityBuilder来操作的

	@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING; // init
beforeInit();
init(); buildState = BuildState.CONFIGURING; // configure
beforeConfigure();
configure(); buildState = BuildState.BUILDING; // build
O result = performBuild(); buildState = BuildState.BUILT; return result;
}
}

大致可以分为三步走,下面笔者就按照上述的三步一一分开剖析

初始化阶段

分为beforeInit()init()两个操作

beforeInit()

beforeInit()初始化前的操作,默认是空的,可供用户去复写

init()

init()初始化方法比较有意思了,看下其源码

	private void init() throws Exception {
// WebSecurity内的configurer是WebSecurityConfigurer类型的
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); // 遍历调用公用的init()方法,关注此处的this指代WebSecurity对象
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
} // 候补
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}

好,笔者来看下configurer#init()方法,此处只针对WebSecurityConfigurer接口的初始化。即使用户没有去实现该接口,springboot也会默认有个类去实现

@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration { @Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter { } }

很明显,就是继承WebSecurityConfigurerAdapter类来实现的,那我们看下其init()方法的操作

	public void init(final WebSecurity web) throws Exception {
// important
final HttpSecurity http = getHttp();
// configure interceptor?
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}

上述的代码比较关键,笔者按两个小步骤讲述一下

1.创建HttpSecurity对象,贴出源码仔细分析下

	protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
} // 配置事件发行器
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); // 获取父级认证管理器,涉及configure(AuthenticationManagerBuilder auth)方法复写
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects(); // 创建HttpSecurity对象
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
// 是否屏蔽默认配置,默认为false
if (!disableDefaults) {
// @formatter:off
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
// 加载spring.factories中以AbstractHttpConfigurer作为Key的类型集合
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader); for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
// 供用户自定义配置HTTP安全配置,默认支持表单和Basic方式提交认证信息
configure(http);
return http;
}

代码信息过多,不一一分析了,springboot推荐用户复写以下两个方法

  • configure(AuthenticationManagerBuilder auth) 配置认证管理器,用户信息读取方式、加密方式均可通过此方法配置
  • configure(HttpSecurity http) 配置http服务,路径拦截、csrf保护等等均可通过此方法配置

2.将HttpSecurity放入WebSecurity中,细看发现HttpSecurity是用来创建FilterChain过滤链的。并尝试塞入一个FilterSecurityInterceptor拦截器,默认一般都是会配置的。

配置阶段

分为beforeConfigure()configure()阶段

beforeConfigure()

配置前的操作,供用户去复写

configure()

遍历其下的所有的WebSecurityConfigurerAdapter的实现类,统一调用configure(WebSecurity web)配置。很明显,用户也可以复写方法来配置,比如对HttpSecurity默认的配置不满意,也可以通过此类来复写之;屏蔽一些URL的访问等等。

创建阶段

真正的创建Filter对象是由performBuild()方法执行的,别看源码很长,其实就一个意思,通过HttpSecurity对象来创建Filter过滤链

	@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
// 用户可通过调用websecurity.ignoring()方法来屏蔽一些访问路径
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
// HttpSecurity#build()会被执行,执行逻辑就跟本文的WebSecurity一模一样
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
// 防火墙配置
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy; postBuildAction.run();
return result;
}

具体的用户可自行去阅读

小结

本文主要讲述了WebSecurity与HttpSecurity之间的关系以及如何被创建,其实都是一样的,它们都是AbstractConfiguredSecurityBuilder的复写类,核心都是会执行其中的build()模板方法来创建过滤链。笔者或者读者只需要复写其中的几个重要方法便可实现简单的安全配置。希望本文对大家有用

springboot情操陶冶-web配置(九)的更多相关文章

  1. springboot情操陶冶-web配置(七)

    参数校验通常是OpenApi必做的操作,其会对不合法的输入做统一的校验以防止恶意的请求.本文则对参数校验这方面作下简单的分析 spring.factories 读者应该对此文件加以深刻的印象,很多sp ...

  2. springboot情操陶冶-web配置(四)

    承接前文springboot情操陶冶-web配置(三),本文将在DispatcherServlet应用的基础上谈下websocket的使用 websocket websocket的简单了解可见维基百科 ...

  3. springboot情操陶冶-web配置(二)

    承接前文springboot情操陶冶-web配置(一),在分析mvc的配置之前先了解下其默认的错误界面是如何显示的 404界面 springboot有个比较有趣的配置server.error.whit ...

  4. springboot情操陶冶-web配置(三)

    承接前文springboot情操陶冶-web配置(二),本文将在前文的基础上分析下mvc的相关应用 MVC简单例子 直接编写一个Controller层的代码,返回格式为json package com ...

  5. springboot情操陶冶-web配置(一)

    承接前文springboot情操陶冶-@SpringBootApplication注解解析,在前文讲解的基础上依次看下web方面的相关配置 环境包依赖 在pom.xml文件中引入web依赖,炒鸡简单, ...

  6. springboot情操陶冶-web配置(八)

    本文关注应用的安全方面,涉及校验以及授权方面,以springboot自带的security板块作为讲解的内容 实例 建议用户可直接路由至博主的先前博客spring security整合cas方案.本文 ...

  7. springboot情操陶冶-web配置(六)

    本文则针对数据库的连接配置作下简单的分析,方便笔者理解以及后续的查阅 栗子当先 以我们经常用的mybatis数据库持久框架来操作mysql服务为例 环境依赖 1.JDK v1.8+ 2.springb ...

  8. springboot情操陶冶-web配置(五)

    本文讲讲mvc的异常处理机制,方便查阅以及编写合理的异常响应方式 入口例子 很简单,根据之前的文章,我们只需要复写WebMvcConfigurer接口的异常添加方法即可,如下 1.创建简单的异常处理类 ...

  9. springboot情操陶冶-@SpringBootApplication注解解析

    承接前文springboot情操陶冶-@Configuration注解解析,本文将在前文的基础上对@SpringBootApplication注解作下简单的分析 @SpringBootApplicat ...

随机推荐

  1. bzoj 2653 middle 二分答案 主席树判定

    判断中位数是否可行需要将当前的解作为分界,大于其的置为1,小于为-1,然后b-c必选,ab,cd可不选,这个用线段树判定就好 但不能每次跑,所以套主席树,按权值排序,构建主席树,更新时将上一个节点改为 ...

  2. 一步一步理解 python web 框架,才不会从入门到放弃 -- 简单登录页面

    上一节,我们基本了解了 Django 的一些配置,这一节,我们将通过一个简单的登录页面,进一步学习 Django 的使用. 新建项目 首先,新建一个 Django 项目,记得别弄错了哦. settin ...

  3. SQL Android

    SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少. 一般有以下几个关键步骤: 1.创建数据库 2.创建表 3.操作:增删改查 4.关闭数据库 5.删除表(非必选) SQLite ...

  4. 再不了解PostgreSQL,你就晚了之PostgreSQL主从流复制部署

    前言 在MySQL被收购之后,虽然有其替代品为: MariaDB,但是总感觉心里有点膈应.大家发现了另一款开源的数据库: PostgreSQL. 虽然centos自带版本9.2也可以用,但是最近的几次 ...

  5. ReentrantLock之公平锁源码分析

    本文分析的ReentrantLock所对应的Java版本为JDK8. 在阅读本文前,读者应该知道什么是CAS.自旋. 本文大纲 1.ReentrantLock公平锁简介 2.AQS 3.lock方法 ...

  6. TensorFlow从1到2(二)续讲从锅炉工到AI专家

    图片样本可视化 原文第四篇中,我们介绍了官方的入门案例MNIST,功能是识别手写的数字0-9.这是一个非常基础的TensorFlow应用,地位相当于通常语言学习的"Hello World!& ...

  7. 搜狗输入法与VS快捷键有冲突_处理办法

    前言:搜狗输入法是大家常用的文字输入工具,但是在开启输入法的时候,VS的一些快捷键无法正常使用,如智能提示快捷键:Ctrl+.,这就非常尴尬了,除非把输入法切换成英文或者卸载搜狗改别的输入法,一个是切 ...

  8. MySQL 四种隔离级别

    什么是事务 事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做. 事务的结束有 ...

  9. EasyToLearnDesignPattern

    简单上手设计模式 GITHUB:https://github.com/Fisher-Joe/EasyToLearnDesignPattern 本文旨在使用最简单的语言,最简单的代码让人学习设计模式(最 ...

  10. 关于web资金系统提现安全保护,防止极快的重复并发请求导致重复提现的解决思路

    关于WEB金融系统中的提现安全问题很多人没有深入思想,导致有漏洞,常常会遇到有些人遇到被攻击到导资金损失的麻烦,     其实要彻底解决重复并发请求 导致重复提现问题,是需要花点心思的,并没有看起来的 ...