SpringSecurity的@EnableWebSecurity注解詳解
@EnableWebSecurity
@EnableWebSecurity是開啟SpringSecurity的默認(rèn)行為,它的上面有一個Import注解導(dǎo)入了WebSecurityConfiguration類,也就是說我們加上了@EnableWebSecurity這個注解,就是往IOC容器中注入了WebSecurityConfiguration這個類。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class})
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}它還有一個debug的功能,如果設(shè)置為true,則開啟debug功能,每個經(jīng)過那些過濾器都會被展示出來。

WebSecurityConfiguration
WebSecurityConfiguration用來配置初始化webSecurity的,在setFilterChainProxySecurityConfigurer方法中,它以配置SpringSecurity時繼承自WebSecurityConfigurerAdapter的配置類來初始化SecurityConfigurer列表,來啟用所需的安全策略
@Autowired(
required = false
)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
this.webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if (this.debugEnabled != null) {
this.webSecurity.debug(this.debugEnabled);
}
webSecurityConfigurers.sort(WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
Iterator var5;
SecurityConfigurer config;
for(var5 = webSecurityConfigurers.iterator(); var5.hasNext(); previousConfig = config) {
config = (SecurityConfigurer)var5.next();
Integer order = WebSecurityConfiguration.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;
}
var5 = webSecurityConfigurers.iterator();
while(var5.hasNext()) {
config = (SecurityConfigurer)var5.next();
//將配置的每一個SecurityConfigurer列表傳遞給 webSecurity
this.webSecurity.apply(config);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}WebSecurityConfiguration這個類的創(chuàng)建流程也是經(jīng)過spring容器初始化的那一整套。

因?yàn)槲覀兣渲玫腟pringSecurityConfig這個類,繼承了WebSecurityConfigurerAdapter,它又實(shí)現(xiàn)了SecurityConfigurer這個接口,所以在配置的時候,能拿到我們這個配置類里面的信息,具體如圖:

創(chuàng)建過濾器鏈
//提供一個名叫springSecurityFilterChain的bean,返回一個Filter對象
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
//如果沒有配置過Spring Security,則會議WebSecurityConfigurerAdapter中的配置作為默認(rèn),上面能拿到我們的配置,因此就不走這段邏輯
WebSecurityConfigurerAdapter
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}以前在Spring的配置中,會有一個web.xml,在里面配置過濾器,但是現(xiàn)在SpringBoot已經(jīng)自動配置了web.xml. DelegatingFilterProxy是Spring提供的一個標(biāo)準(zhǔn)Servlet Filter代理,并代理改bean提供的過濾器,也就是說,在這個配置中,最終起作用的過濾器是什么完全取決于springSecurityFilterChain。
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name()))
.collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));
}
}前面說的springSecurityFilterChain是由 webSecurity.build()這個創(chuàng)建的,最終調(diào)用的是doBuild方法,是由AbstractConfiguredSecurityBuilder提供的
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}AbstractConfiguredSecurityBuilder的doBuild調(diào)用的是WebSecurity的performBuild()方法
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
//安裝狀態(tài)依次執(zhí)行相應(yīng)的方法
buildState = BuildState.INITIALIZING;
beforeInit();
//初始化狀態(tài),通過調(diào)用WebSecurityConfigurerAdapter的init,將所有HttpSecurity添加到WebSecurity里
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
//在BUILDING階段調(diào)用WebSecurity的performBuild方法
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}在init方法中,初始化狀態(tài),通過調(diào)用WebSecurityConfigurerAdapter的init,將所有HttpSecurity添加到WebSecurity里

在performBuild方法中,SpringSecurity完成了所有過濾器的創(chuàng)建,最終返回一個過濾器鏈代理類filterChainProxy.
@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);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
//簡單來說,就是每一個HttpSecurity生成一個過濾器鏈,HttpSecurity則來自我們配置的WebSecurityConfigure
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
filterChainProxy間接繼承了FIlter,可以作為真正的過濾器使用,它會攜帶若干條過濾器鏈,并在承擔(dān)過濾器職責(zé)時,將其派發(fā)到所有過濾器鏈的每一個過濾器上。
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (clearContext) {
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
doFilterInternal(request, response, chain);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
else {
doFilterInternal(request, response, chain);
}
}doFilterInternal是真正執(zhí)行虛擬過濾器鏈邏輯的方法
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//附上Spring Security提供的HTTP防火墻
FirewalledRequest fwRequest = firewall
.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse fwResponse = firewall
.getFirewalledResponse((HttpServletResponse) response);
//按照配置的RequestMatcher,決定每一個請求會經(jīng)過那些過濾器
List<Filter> filters = getFilters(fwRequest);
if (filters == null || filters.size() == 0) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(fwRequest)
+ (filters == null ? " has no matching filters"
: " has an empty filter list"));
}
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return;
}
//所有的過濾器合并成一條虛擬的過濾器鏈
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
//模擬過濾器的執(zhí)行流程,執(zhí)行整條過濾器鏈
vfc.doFilter(fwRequest, fwResponse);
}
private static class VirtualFilterChain implements FilterChain {
private final FilterChain originalChain;
private final List<Filter> additionalFilters;
private final FirewalledRequest firewalledRequest;
private final int size;
private int currentPosition = 0;
private VirtualFilterChain(FirewalledRequest firewalledRequest,
FilterChain chain, List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
this.firewalledRequest = firewalledRequest;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (currentPosition == size) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " reached end of additional filter chain; proceeding with original chain");
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
執(zhí)行過濾器鏈后,調(diào)用真實(shí)的FilterChain,完成原生過濾器的剩余邏輯
originalChain.doFilter(request, response);
}
else {
currentPosition++;
Filter nextFilter = additionalFilters.get(currentPosition - 1);
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " at position " + currentPosition + " of " + size
+ " in additional filter chain; firing Filter: '"
+ nextFilter.getClass().getSimpleName() + "'");
}
//通過改變下表回調(diào)的方式,按照順序執(zhí)行每一個過濾器
nextFilter.doFilter(request, response, this);
}
}
}

到此這篇關(guān)于SpringSecurity的@EnableWebSecurity注解詳解的文章就介紹到這了,更多相關(guān)@EnableWebSecurity注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot+maven多環(huán)境動態(tài)配置及編譯失敗的解決方案(步驟詳解)
這篇文章主要介紹了springboot+maven多環(huán)境動態(tài)配置及編譯失敗的解決方案,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-11-11
IntelliJ IDEA將導(dǎo)入的項(xiàng)目轉(zhuǎn)成maven項(xiàng)目
這篇文章主要介紹了IntelliJ IDEA將導(dǎo)入的項(xiàng)目轉(zhuǎn)成maven項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
SpringBoot3 整合Docker-Compose的實(shí)現(xiàn)步驟
本文主要介紹了SpringBoot3 整合Docker-Compose的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-08-08
詳解spring cloud如何使用spring-test進(jìn)行單元測試
這篇文章主要介紹了spring cloud如何使用spring-test進(jìn)行單元測試,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Java多線程事務(wù)回滾@Transactional失效處理方案
這篇文章主要介紹了Java多線程事務(wù)回滾@Transactional失效處理方案,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-08-08
Java裝飾器設(shè)計(jì)模式_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了Java裝飾器設(shè)計(jì)模式的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-05-05
Prometheus監(jiān)控Springboot程序的實(shí)現(xiàn)方法
這篇文章主要介紹了Prometheus監(jiān)控Springboot程序的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
SpringSecurity整合springBoot、redis實(shí)現(xiàn)登錄互踢功能
這篇文章主要介紹了SpringSecurity整合springBoot、redis實(shí)現(xiàn)登錄互踢,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-05-05

