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容器初始化的那一整套。
因為我們配置的SpringSecurityConfig這個類,繼承了WebSecurityConfigurerAdapter,它又實現(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)用真實的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實現(xiàn)輸出回環(huán)數(shù)(螺旋矩陣)的方法示例
這篇文章主要介紹了Java實現(xiàn)輸出回環(huán)數(shù)(螺旋矩陣)的方法,涉及java針對數(shù)組的遍歷、判斷、輸出等相關(guān)操作技巧,需要的朋友可以參考下2017-12-12Java后臺返回和處理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è)計工具(非開源),具有一個獨立的開發(fā)工具產(chǎn)品和基于不同開發(fā)工具如Eclipse、NetBeans等的開發(fā)插件,本文給大家介紹JFormDesigner安裝破解教程,感興趣的朋友一起看看吧2023-12-12Spring Security動態(tài)權(quán)限的實現(xiàn)方法詳解
這篇文章主要和小伙伴們簡單介紹下 Spring Security 中的動態(tài)權(quán)限方案,以便于小伙伴們更好的理解 TienChin 項目中的權(quán)限方案,感興趣的可以了解一下2022-06-06