SpringSecurity在SpringBoot中的自動(dòng)裝配過程
從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})
//提供拓展,沒有提供以下三個(gè)實(shí)現(xiàn)類才使用默認(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開頭的配置文件
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 {
//如果沒有配置,用戶名默認(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的過程由spring自動(dòng)實(shí)現(xiàn).spring自動(dòng)注入DelegatingFilterProxy對(duì)象,這樣就可以將security中的過濾器切到spring中,在請求的時(shí)候會(huì)被DelegatingFilterProxyRegistrationBean攔截,然后去執(zhí)行security中的過濾器鏈。
@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的過程實(shí)際是通過ServletContextInitializer接口實(shí)現(xiàn)的,有一個(gè)方法onStartup,有一個(gè)實(shí)現(xiàn)類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 {
//注冊
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;
}
}
注冊邏輯在父類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 {
//在其父類AbstractFilterRegistrationBean中配置攔截/*請求
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è)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot多環(huán)境配置與啟動(dòng)banner修改的操作詳解
這篇文章主要介紹了如何自定義SpringBoot的啟動(dòng)banner和設(shè)置多環(huán)境配置,第一部分教如何通過在線工具生成并配置自定義banner,增強(qiáng)項(xiàng)目辨識(shí)度,第二部分解釋了使用Profile來管理不同環(huán)境(開發(fā)、測試、生產(chǎn))的配置,需要的朋友可以參考下2025-10-10
詳解springboot設(shè)置默認(rèn)參數(shù)Springboot.setDefaultProperties(map)不生效解決
這篇文章主要介紹了詳解springboot設(shè)置默認(rèn)參數(shù)Springboot.setDefaultProperties(map)不生效解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
如何巧用HashMap一行代碼統(tǒng)計(jì)單詞出現(xiàn)次數(shù)詳解
這篇文章主要給大家介紹了關(guān)于如何巧用HashMap一行代碼統(tǒng)計(jì)單詞出現(xiàn)次數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Java靜態(tài)代理與動(dòng)態(tài)代理案例詳解
這篇文章主要介紹了Java靜態(tài)代理與動(dòng)態(tài)代理案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07

