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)文章
Java實(shí)現(xiàn)文件分片上傳接口的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實(shí)現(xiàn)文件分片上傳的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2022-07-07
Java實(shí)現(xiàn)輸出回環(huán)數(shù)(螺旋矩陣)的方法示例
這篇文章主要介紹了Java實(shí)現(xiàn)輸出回環(huán)數(shù)(螺旋矩陣)的方法,涉及java針對數(shù)組的遍歷、判斷、輸出等相關(guān)操作技巧,需要的朋友可以參考下2017-12-12
Java后臺返回和處理JSon數(shù)據(jù)的方法步驟
這篇文章主要介紹了Java后臺返回和處理JSon數(shù)據(jù)的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
關(guān)于JFormDesigner的安裝及破姐超詳細(xì)教程
JFormDesigner是一種先進(jìn)的圖形用戶界面Swing?的設(shè)計工具(非開源),具有一個獨(dú)立的開發(fā)工具產(chǎn)品和基于不同開發(fā)工具如Eclipse、NetBeans等的開發(fā)插件,本文給大家介紹JFormDesigner安裝破解教程,感興趣的朋友一起看看吧2023-12-12
Spring Security動態(tài)權(quán)限的實(shí)現(xiàn)方法詳解
這篇文章主要和小伙伴們簡單介紹下 Spring Security 中的動態(tài)權(quán)限方案,以便于小伙伴們更好的理解 TienChin 項(xiàng)目中的權(quán)限方案,感興趣的可以了解一下2022-06-06
JavaWeb如何實(shí)現(xiàn)限制單個賬號多處登錄
這篇文章主要介紹了JavaWeb如何實(shí)現(xiàn)限制單個賬號多處登錄問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08

