SpringSecurity在SpringBoot中的自動(dòng)裝配過(guò)程
從SpringBoot的自動(dòng)裝配原理入手
找到META-INFO下的spring.factories文件
SpringSecurity作為Spring的親兒子,自然在spring-boot-autoconfigure下的spring.factories文件中配置了
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
UserDetailsServiceAutoConfiguration
@Configuration( proxyBeanMethods = false ) //條件注解 @ConditionalOnClass({AuthenticationManager.class}) @ConditionalOnBean({ObjectPostProcessor.class}) //提供拓展,沒(méi)有提供以下三個(gè)實(shí)現(xiàn)類(lèi)才使用默認(rèn)的 @ConditionalOnMissingBean( value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class}, type = {"org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector"} ) public class UserDetailsServiceAutoConfiguration { //密碼不加密表示 private static final String NOOP_PASSWORD_PREFIX = "{noop}"; private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$"); private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class); public UserDetailsServiceAutoConfiguration() { } @Bean @ConditionalOnMissingBean( type = {"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository"} ) @Lazy public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) { //讀取以spring.security開(kāi)頭的配置文件 SecurityProperties.User user = properties.getUser(); List<String> roles = user.getRoles(); return new InMemoryUserDetailsManager(new UserDetails[]{User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()}); } private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) { String password = user.getPassword(); if (user.isPasswordGenerated()) { logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword())); } return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password; } }
//標(biāo)記讀取配置文件夾信息 @ConfigurationProperties( prefix = "spring.security" ) public class SecurityProperties { public static final int BASIC_AUTH_ORDER = 2147483642; public static final int IGNORED_ORDER = Integer.MIN_VALUE; public static final int DEFAULT_FILTER_ORDER = -100; private final Filter filter = new Filter(); private User user = new User(); public SecurityProperties() { } public User getUser() { return this.user; } public Filter getFilter() { return this.filter; } public static class User { //如果沒(méi)有配置,用戶(hù)名默認(rèn)user,密碼uuid private String name = "user"; private String password = UUID.randomUUID().toString(); private List<String> roles = new ArrayList(); private boolean passwordGenerated = true; public User() { } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public String getPassword() { return this.password; } public void setPassword(String password) { if (StringUtils.hasLength(password)) { this.passwordGenerated = false; this.password = password; } } public List<String> getRoles() { return this.roles; } public void setRoles(List<String> roles) { this.roles = new ArrayList(roles); } public boolean isPasswordGenerated() { return this.passwordGenerated; } } public static class Filter { private int order = -100; private Set<DispatcherType> dispatcherTypes; public Filter() { this.dispatcherTypes = new HashSet(Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST)); } public int getOrder() { return this.order; } public void setOrder(int order) { this.order = order; } public Set<DispatcherType> getDispatcherTypes() { return this.dispatcherTypes; } public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) { this.dispatcherTypes = dispatcherTypes; } } }
SecurityFilterAutoConfiguration
約定大于配置,這里的內(nèi)容就相當(dāng)于在web.xml配置文件中配置springSecurityFilterChain的過(guò)程由spring自動(dòng)實(shí)現(xiàn).spring自動(dòng)注入DelegatingFilterProxy對(duì)象,這樣就可以將security中的過(guò)濾器切到spring中,在請(qǐng)求的時(shí)候會(huì)被DelegatingFilterProxyRegistrationBean攔截,然后去執(zhí)行security中的過(guò)濾器鏈。
@Configuration( proxyBeanMethods = false ) //web項(xiàng)目才加載 @ConditionalOnWebApplication( type = Type.SERVLET ) @EnableConfigurationProperties({SecurityProperties.class}) @ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class}) //SecurityAutoConfiguration之后執(zhí)行 @AutoConfigureAfter({SecurityAutoConfiguration.class}) public class SecurityFilterAutoConfiguration { //名字 private static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain"; public SecurityFilterAutoConfiguration() { } /** * 創(chuàng)建DelegatingFilterProxyRegistrationBean對(duì)象注入到spring容器中 * @param securityProperties * @return */ @Bean @ConditionalOnBean( name = {"springSecurityFilterChain"} ) public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) { //創(chuàng)建DelegatingFilterProxy對(duì)象 DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]); registration.setOrder(securityProperties.getFilter().getOrder()); registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties)); return registration; } private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) { return securityProperties.getFilter().getDispatcherTypes() == null ? null : (EnumSet)securityProperties.getFilter().getDispatcherTypes().stream().map((type) -> { return DispatcherType.valueOf(type.name()); }).collect(Collectors.toCollection(() -> { return EnumSet.noneOf(DispatcherType.class); })); } }
創(chuàng)建DelegatingFilterProxy的過(guò)程實(shí)際是通過(guò)ServletContextInitializer接口實(shí)現(xiàn)的,有一個(gè)方法onStartup,有一個(gè)實(shí)現(xiàn)類(lèi)RegistrationBean
public abstract class RegistrationBean implements ServletContextInitializer, Ordered { private static final Log logger = LogFactory.getLog(RegistrationBean.class); private int order = Integer.MAX_VALUE; private boolean enabled = true; public RegistrationBean() { } public final void onStartup(ServletContext servletContext) throws ServletException { String description = this.getDescription(); if (!this.isEnabled()) { logger.info(StringUtils.capitalize(description) + " was not registered (disabled)"); } else { //注冊(cè) this.register(description, servletContext); } } protected abstract String getDescription(); protected abstract void register(String description, ServletContext servletContext); public void setEnabled(boolean enabled) { this.enabled = enabled; } public boolean isEnabled() { return this.enabled; } public void setOrder(int order) { this.order = order; } public int getOrder() { return this.order; } }
注冊(cè)邏輯在父類(lèi)DynamicRegistrationBean中
public abstract class DynamicRegistrationBean<D extends Registration.Dynamic> extends RegistrationBean { private static final Log logger = LogFactory.getLog(RegistrationBean.class); private String name; private boolean asyncSupported = true; private Map<String, String> initParameters = new LinkedHashMap(); public DynamicRegistrationBean() { } public void setName(String name) { Assert.hasLength(name, "Name must not be empty"); this.name = name; } public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } public boolean isAsyncSupported() { return this.asyncSupported; } public void setInitParameters(Map<String, String> initParameters) { Assert.notNull(initParameters, "InitParameters must not be null"); this.initParameters = new LinkedHashMap(initParameters); } public Map<String, String> getInitParameters() { return this.initParameters; } public void addInitParameter(String name, String value) { Assert.notNull(name, "Name must not be null"); this.initParameters.put(name, value); } protected final void register(String description, ServletContext servletContext) { //生成DelegatingFilterProxy對(duì)象 D registration = this.addRegistration(description, servletContext); if (registration == null) { logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)"); } else { //在其父類(lèi)AbstractFilterRegistrationBean中配置攔截/*請(qǐng)求 this.configure(registration); } } protected abstract D addRegistration(String description, ServletContext servletContext); protected void configure(D registration) { registration.setAsyncSupported(this.asyncSupported); if (!this.initParameters.isEmpty()) { registration.setInitParameters(this.initParameters); } } protected final String getOrDeduceName(Object value) { return this.name != null ? this.name : Conventions.getVariableName(value); } }
跟蹤代碼發(fā)現(xiàn)實(shí)際調(diào)用的了DelegatingFilterProxyRegistrationBean的getFilter方法
public class DelegatingFilterProxyRegistrationBean extends AbstractFilterRegistrationBean<DelegatingFilterProxy> implements ApplicationContextAware { private ApplicationContext applicationContext; private final String targetBeanName; public DelegatingFilterProxyRegistrationBean(String targetBeanName, ServletRegistrationBean<?>... servletRegistrationBeans) { super(servletRegistrationBeans); Assert.hasLength(targetBeanName, "TargetBeanName must not be null or empty"); this.targetBeanName = targetBeanName; this.setName(targetBeanName); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } protected String getTargetBeanName() { return this.targetBeanName; } //創(chuàng)建DelegatingFilterProxy實(shí)例對(duì)象 public DelegatingFilterProxy getFilter() { return new DelegatingFilterProxy(this.targetBeanName, this.getWebApplicationContext()) { protected void initFilterBean() throws ServletException { } }; } private WebApplicationContext getWebApplicationContext() { Assert.notNull(this.applicationContext, "ApplicationContext be injected"); Assert.isInstanceOf(WebApplicationContext.class, this.applicationContext); return (WebApplicationContext)this.applicationContext; } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot啟動(dòng)security后如何關(guān)閉彈出的/login頁(yè)面
- Springboot整合SpringSecurity的完整案例詳解
- SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用
- SpringBoot整合新版SpringSecurity完整過(guò)程
- SpringBoot集成Swagger使用SpringSecurity控制訪問(wèn)權(quán)限問(wèn)題
- SpringBoot集成SpringSecurity安全框架方式
- Springbootadmin與security沖突問(wèn)題及解決
- SpringBoot整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫(kù)登錄及權(quán)限控制功能
- SpringBoot配置Spring?Security的實(shí)現(xiàn)示例
相關(guān)文章
mybatis-plus查詢(xún)無(wú)數(shù)據(jù)問(wèn)題及解決
這篇文章主要介紹了mybatis-plus查詢(xún)無(wú)數(shù)據(jù)問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12Spring?Boot中的微信支付全過(guò)程(小程序)
微信支付是企業(yè)級(jí)項(xiàng)目中經(jīng)常使用到的功能,作為后端開(kāi)發(fā)人員,完整地掌握該技術(shù)是十分有必要的。今天通過(guò)本文給大家介紹Spring?Boot中的微信支付全過(guò)程,感興趣的朋友一起看看吧2022-05-05springboot結(jié)合JWT實(shí)現(xiàn)單點(diǎn)登錄的示例
本文主要介紹了springboot結(jié)合JWT實(shí)現(xiàn)單點(diǎn)登錄的示例,包括生成Token、驗(yàn)證Token及使用Redis存儲(chǔ)Token,具有一定的參考價(jià)值,感興趣的可以了解一下2025-01-01SpringBoot自定義注解實(shí)現(xiàn)Token校驗(yàn)的方法
這篇文章主要介紹了SpringBoot自定義注解實(shí)現(xiàn)Token校驗(yàn)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03SpringBoot對(duì)PDF進(jìn)行模板內(nèi)容填充與電子簽名合并詳解
這篇文章主要為大家詳細(xì)介紹了SpringBoot對(duì)PDF進(jìn)行模板內(nèi)容填充與電子簽名合并的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考下2023-12-12Java樹(shù)形結(jié)構(gòu)遞歸查詢(xún)方式
文章介紹了Java中實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)遞歸查詢(xún)的方法,首先找出所有的根節(jié)點(diǎn),然后通過(guò)循環(huán)遍歷根節(jié)點(diǎn),找到每個(gè)根節(jié)點(diǎn)的子節(jié)點(diǎn),最終構(gòu)建完整的樹(shù)形結(jié)構(gòu),這是一種有效的遞歸查詢(xún)思路,適用于需要層次化展示數(shù)據(jù)的場(chǎng)景2024-12-12Intellij IDEA 添加jar包的三種方式(小結(jié))
這篇文章主要介紹了Intellij IDEA 添加jar包的三種方式(小結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08