SpringSecurity的@EnableWebSecurity注解詳解
@EnableWebSecurity
@EnableWebSecurity是開(kāi)啟SpringSecurity的默認(rèn)行為,它的上面有一個(gè)Import注解導(dǎo)入了WebSecurityConfiguration類,也就是說(shuō)我們加上了@EnableWebSecurity這個(gè)注解,就是往IOC容器中注入了WebSecurityConfiguration這個(gè)類。
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class}) @EnableGlobalAuthentication @Configuration public @interface EnableWebSecurity { boolean debug() default false; }
它還有一個(gè)debug的功能,如果設(shè)置為true,則開(kāi)啟debug功能,每個(gè)經(jīng)過(guò)那些過(guò)濾器都會(huì)被展示出來(lái)。
WebSecurityConfiguration
WebSecurityConfiguration用來(lái)配置初始化webSecurity的,在setFilterChainProxySecurityConfigurer方法中,它以配置SpringSecurity時(shí)繼承自WebSecurityConfigurerAdapter的配置類來(lái)初始化SecurityConfigurer列表,來(lái)啟用所需的安全策略
@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(); //將配置的每一個(gè)SecurityConfigurer列表傳遞給 webSecurity this.webSecurity.apply(config); } this.webSecurityConfigurers = webSecurityConfigurers; }
WebSecurityConfiguration這個(gè)類的創(chuàng)建流程也是經(jīng)過(guò)spring容器初始化的那一整套。
因?yàn)槲覀兣渲玫腟pringSecurityConfig這個(gè)類,繼承了WebSecurityConfigurerAdapter,它又實(shí)現(xiàn)了SecurityConfigurer這個(gè)接口,所以在配置的時(shí)候,能拿到我們這個(gè)配置類里面的信息,具體如圖:
創(chuàng)建過(guò)濾器鏈
//提供一個(gè)名叫springSecurityFilterChain的bean,返回一個(gè)Filter對(duì)象 @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty(); if (!hasConfigurers) { //如果沒(méi)有配置過(guò)Spring Security,則會(huì)議WebSecurityConfigurerAdapter中的配置作為默認(rèn),上面能拿到我們的配置,因此就不走這段邏輯 WebSecurityConfigurerAdapter WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); webSecurity.apply(adapter); } return webSecurity.build(); }
以前在Spring的配置中,會(huì)有一個(gè)web.xml,在里面配置過(guò)濾器,但是現(xiàn)在SpringBoot已經(jīng)自動(dòng)配置了web.xml. DelegatingFilterProxy是Spring提供的一個(gè)標(biāo)準(zhǔn)Servlet Filter代理,并代理改bean提供的過(guò)濾器,也就是說(shuō),在這個(gè)配置中,最終起作用的過(guò)濾器是什么完全取決于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))); } }
前面說(shuō)的springSecurityFilterChain是由 webSecurity.build()這個(gè)創(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),通過(guò)調(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),通過(guò)調(diào)用WebSecurityConfigurerAdapter的init,將所有HttpSecurity添加到WebSecurity里
在performBuild方法中,SpringSecurity完成了所有過(guò)濾器的創(chuàng)建,最終返回一個(gè)過(guò)濾器鏈代理類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)); } //簡(jiǎn)單來(lái)說(shuō),就是每一個(gè)HttpSecurity生成一個(gè)過(guò)濾器鏈,HttpSecurity則來(lái)自我們配置的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,可以作為真正的過(guò)濾器使用,它會(huì)攜帶若干條過(guò)濾器鏈,并在承擔(dān)過(guò)濾器職責(zé)時(shí),將其派發(fā)到所有過(guò)濾器鏈的每一個(gè)過(guò)濾器上。
@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í)行虛擬過(guò)濾器鏈邏輯的方法
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,決定每一個(gè)請(qǐng)求會(huì)經(jīng)過(guò)那些過(guò)濾器 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; } //所有的過(guò)濾器合并成一條虛擬的過(guò)濾器鏈 VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters); //模擬過(guò)濾器的執(zhí)行流程,執(zhí)行整條過(guò)濾器鏈 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í)行過(guò)濾器鏈后,調(diào)用真實(shí)的FilterChain,完成原生過(guò)濾器的剩余邏輯 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() + "'"); } //通過(guò)改變下表回調(diào)的方式,按照順序執(zhí)行每一個(gè)過(guò)濾器 nextFilter.doFilter(request, response, this); } } }
到此這篇關(guān)于SpringSecurity的@EnableWebSecurity注解詳解的文章就介紹到這了,更多相關(guān)@EnableWebSecurity注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)文件分片上傳接口的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Java語(yǔ)言實(shí)現(xiàn)文件分片上傳的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2022-07-07Java實(shí)現(xiàn)輸出回環(huán)數(shù)(螺旋矩陣)的方法示例
這篇文章主要介紹了Java實(shí)現(xiàn)輸出回環(huán)數(shù)(螺旋矩陣)的方法,涉及java針對(duì)數(shù)組的遍歷、判斷、輸出等相關(guān)操作技巧,需要的朋友可以參考下2017-12-12Java后臺(tái)返回和處理JSon數(shù)據(jù)的方法步驟
這篇文章主要介紹了Java后臺(tái)返回和處理JSon數(shù)據(jù)的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09關(guān)于JFormDesigner的安裝及破姐超詳細(xì)教程
JFormDesigner是一種先進(jìn)的圖形用戶界面Swing?的設(shè)計(jì)工具(非開(kāi)源),具有一個(gè)獨(dú)立的開(kāi)發(fā)工具產(chǎn)品和基于不同開(kāi)發(fā)工具如Eclipse、NetBeans等的開(kāi)發(fā)插件,本文給大家介紹JFormDesigner安裝破解教程,感興趣的朋友一起看看吧2023-12-12Spring Security動(dòng)態(tài)權(quán)限的實(shí)現(xiàn)方法詳解
這篇文章主要和小伙伴們簡(jiǎn)單介紹下 Spring Security 中的動(dòng)態(tài)權(quán)限方案,以便于小伙伴們更好的理解 TienChin 項(xiàng)目中的權(quán)限方案,感興趣的可以了解一下2022-06-06JavaWeb如何實(shí)現(xiàn)限制單個(gè)賬號(hào)多處登錄
這篇文章主要介紹了JavaWeb如何實(shí)現(xiàn)限制單個(gè)賬號(hào)多處登錄問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08