spring-security-源码解析+自定义拓展

1.参考文档

https://docs.spring.io/spring-security/reference/5.7/servlet/architecture.html

1.1.各种filterchain

1.1.1.SecurityFilterChain

1.1.2.springSecurityFilterChain

1.1.3.Security Filters

2.几个重要的注解

2.1.@EnableXXX

  1. @EnableWebMvcSecurity --@deprecated
  2. @EnableWebSecurity
    • import: WebSecurityConfiguration
    • import: HttpSecurityConfiguration
    • import: SpringWebMvcImportSelector ===> WebMvcSecurityConfiguration(略)
    • import: OAuth2ImportSelector(略)
    • @EnableGlobalAuthentication(4)
  3. @EnableGlobalMethodSecurity
    • @EnableGlobalAuthentication(4)
    • import: GlobalMethodSecuritySelector ===> GlobalMethodSecurityConfiguration
  4. @EnableGlobalAuthentication
    • import: AuthenticationConfiguration

2.2.AuthenticationConfiguration

java 复制代码
@Configuration(proxyBeanMethods = false)
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
	private AuthenticationManager authenticationManager;
	@Autowired(required = false)
	private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections.emptyList();
	
	@Autowired
	private ObjectPostProcessor<Object> objectPostProcessor;
	
	//1.
	@Bean
	public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
			ApplicationContext context) {
		LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
		AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);
		//1.1
		DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
				objectPostProcessor, defaultPasswordEncoder);
		if (authenticationEventPublisher != null) {
			result.authenticationEventPublisher(authenticationEventPublisher);
		}
		return result;
	}
	
	public AuthenticationManager getAuthenticationManager() throws Exception {
		//2
		AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class); //from 1
		
		//2.1 apply globalAuthConfigurers
		for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
			authBuilder.apply(config);
		}
		this.authenticationManager = authBuilder.build();
		
		this.authenticationManagerInitialized = true;
		return this.authenticationManager;
	}
	
	/**
	@Bean EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter
	@Bean InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationConfigurerAdapter
	@Bean InitializeAuthenticationProviderBeanManagerConfigurer extends GlobalAuthenticationConfigurerAdapter
	**/
}

2.2.1 ObjectPostProcessorConfiguration

JAVA 复制代码
public class ObjectPostProcessorConfiguration {

	@Bean
	public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
		return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
	}
}

3.webSecurity 流程分析

3.1.@EnableWebSecurity

3.1.1.HttpSecurityConfiguration

java 复制代码
@Configuration(proxyBeanMethods = false)
class HttpSecurityConfiguration {
	@Autowired
	private ObjectPostProcessor<Object> objectPostProcessor;
	
	@Autowired
	private AuthenticationManager authenticationManager;
	
	@Autowired
	private AuthenticationConfiguration authenticationConfiguration;
	
	@Bean(HTTPSECURITY_BEAN_NAME)
	@Scope("prototype") //允许注入多个
	HttpSecurity httpSecurity() throws Exception {
		WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(
				this.context);
		AuthenticationManagerBuilder authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(
				this.objectPostProcessor, passwordEncoder);
		authenticationBuilder.parentAuthenticationManager(authenticationManager());
		authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
		HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
		// @formatter:off
		http
			.csrf(withDefaults())
			.addFilter(new WebAsyncManagerIntegrationFilter())
			.exceptionHandling(withDefaults())
			.headers(withDefaults())
			.sessionManagement(withDefaults())
			.securityContext(withDefaults())
			.requestCache(withDefaults())
			.anonymous(withDefaults())
			.servletApi(withDefaults())
			.apply(new DefaultLoginPageConfigurer<>());
		http.logout(withDefaults());
		// @formatter:on
		applyDefaultConfigurers(http); //1
		return http;
	}
	
	private void applyDefaultConfigurers(HttpSecurity http) throws Exception {
		ClassLoader classLoader = this.context.getClassLoader();
		List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
				.loadFactories(AbstractHttpConfigurer.class, classLoader);
		for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
			http.apply(configurer);
		}
	}
}

3.1.2.WebSecurityConfiguration

java 复制代码
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	private WebSecurity webSecurity;
	
	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
	
	//securityFilterChains, 可有多个httpSecurity.buid()多个;
	@Autowired(required = false)
	private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
	
	@Autowired(required = false) 	
	private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
	
	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;
	
	@Autowired(required = false)
	public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
			ConfigurableListableBeanFactory beanFactory) throws Exception {
		//0.初始化 webSecurity
		this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
		
		//AutowiredWebSecurityConfigurersIgnoreParents内部逻辑:this.beanFactory.getBeansOfType(WebSecurityConfigurer.class);
		List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new AutowiredWebSecurityConfigurersIgnoreParents(
				beanFactory).getWebSecurityConfigurers();
		//webSecurityConfigurers 排序略.....
		
		//1.应用 webSecurityConfigurer;
		for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			this.webSecurity.apply(webSecurityConfigurer);
		}
		this.webSecurityConfigurers = webSecurityConfigurers;
	}
	
	//2. 注入 springSecurityFilterChain
	@Bean(name = "springSecurityFilterChain")
	public Filter springSecurityFilterChain() throws Exception {
		//2.1
		WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
		this.webSecurity.apply(adapter);
			
		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
			//2.2 "springSecurityFilterChain" 添加多个securityFilterChain
			this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
			
			//2.3 添加FilterSecurityInterceptor
			for (Filter filter : securityFilterChain.getFilters()) {
				if (filter instanceof FilterSecurityInterceptor) {
					this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
					break;
				}
			}
		}
		
		//2.4 应用webSecurityCustomizers
		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
			customizer.customize(this.webSecurity);
		}
		return this.webSecurity.build();
	}
	
	//3. 注入privilegeEvaluator: 由webSecurity初始化生成
	@Bean
	public WebInvocationPrivilegeEvaluator privilegeEvaluator() {
		return this.webSecurity.getPrivilegeEvaluator();
	}
	
	//4. 注入webSecurityExpressionHandler: 由webSecurity初始化生成
	@Bean
	public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
		return this.webSecurity.getExpressionHandler();
	}

}

3.2.总体处理流程(粗)

request -> springSecurityFilterChain -> securityFilterChain[s]

3.3.httpSecurity.build() 流程

build

java 复制代码
@Bean // 可选,可配置多个
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf().disable()
	       		.addFilterBefore(xxxAuthenticationFilter(), AbstractPreAuthenticatedProcessingFilter.class)
                .authorizeRequests()
                .expressionHandler(xxxWebExpressionHandler());
        registry
                .antMatchers("/healthcheck/**").permitAll()
                .antMatchers("/**")
                .access("isLoggedIn()");
        return http.build();
}

3.3.1.httpSecurity类图

3.3.2.httpSecurity.build() 源码

java 复制代码
//总体流程
httpSecurity.buid()
	AbstractSecurityBuilder.build()
		AbstractConfiguredSecurityBuilder.doBuild()
			init(); // getConfigurers() --> Collection<SecurityConfigurer<O, B>>
			configure(); // for-each SecurityConfigurer.configure() --> 初始化Filter
			beforeConfigure(); //由HttpSecurity重写: 初始化 authenticationManager
			performBuild()
				httpSecurity.performBuild()

public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
		implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {

	private final RequestMatcherConfigurer requestMatcherConfigurer;

	//a.注入HttpSecurityConfiguration.httpSecurity()设置: 
	private List<OrderedFilter> filters = new ArrayList<>();
	private AuthenticationManager authenticationManager; //set

	protected DefaultSecurityFilterChain performBuild() {
		//a.注入HttpSecurityConfiguration.httpSecurity()设置: 

		//b.用户 注册filterChain时 设置 ; 鉴权时会用到
		ExpressionUrlAuthorizationConfigurer<?> expressionConfigurer = getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
		AuthorizeHttpRequestsConfigurer<?> httpConfigurer = getConfigurer(AuthorizeHttpRequestsConfigurer.class); 
		boolean oneConfigurerPresent = expressionConfigurer == null ^ httpConfigurer == null;
		
		//sort 略....
		List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
		
		return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
	}


}	

3.3.webSecurity.build() 流程

注入demo

java 复制代码
   @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring()
                .antMatchers("/css/**")
                .antMatchers("/html/**")
                .antMatchers("/img/**")
                .antMatchers("/js/**")
                .antMatchers("/libs/**")
                .antMatchers("/version.txt");
    }

3.3.1.WebSecurity类图(略)

见HttpSecurity类图

3.3.2.webSecurity.build() 源码

java 复制代码
//调用总体路径
WebSecurity.build()
		AbstractSecurityBuilder.build()
			AbstractConfiguredSecurityBuilder.doBuild()
				init(); // getConfigurers() --> Collection<SecurityConfigurer<O, B>>
				configure(); // for-each SecurityConfigurer.configure() --> 初始化Filter	
				performBuild();
					WebSecurity.performBuild() //1

public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
		implements SecurityBuilder<Filter>, ApplicationContextAware, ServletContextAware {
	//由用户注入: web.ignoring().antMatchers("/***")
	private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
	private IgnoredRequestConfigurer ignoredRequestRegistry; //new IgnoredRequestConfigurer(ctx)
	

	private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<>();//由WebSecurityConfiguration初始化
	private FilterSecurityInterceptor filterSecurityInterceptor;//由WebSecurityConfiguration初始化
	
	@Autowired
	private HttpFirewall httpFirewall;
	
	@Autowired
	private RequestRejectedHandler requestRejectedHandler;
	private WebInvocationPrivilegeEvaluator privilegeEvaluator; //初始化完成后,被WebSecurityConfiguration @Bean 注入;
	
	private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
	private SecurityExpressionHandler<FilterInvocation> expressionHandler = this.defaultWebSecurityExpressionHandler;//初始化完成后,被WebSecurityConfiguration @Bean 注入;
	private Runnable postBuildAction = () -> {};
	
	@Override
	protected Filter performBuild() throws Exception {
	
		int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
		List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize); //
		List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
		for (RequestMatcher ignoredRequest : this.ignoredRequests) {
			SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
			securityFilterChains.add(securityFilterChain);//将ignoredRequest 添加在第1位
			requestMatcherPrivilegeEvaluatorsEntries
					.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain)); //2
		}
		//for循环build securityFilterChains
		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
			SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
			securityFilterChains.add(securityFilterChain);
			requestMatcherPrivilegeEvaluatorsEntries
					.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain)); 
		}
		
		//3.
		if (this.privilegeEvaluator == null) {
			this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
					requestMatcherPrivilegeEvaluatorsEntries);
		}
		FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
		filterChainProxy.setFirewall(this.httpFirewall);
		filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
		filterChainProxy.afterPropertiesSet();

		Filter result = filterChainProxy;
	
		this.postBuildAction.run();
		return result;
	}
	
	//2
	private RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> getRequestMatcherPrivilegeEvaluatorsEntry(
			SecurityFilterChain securityFilterChain) {
		List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = new ArrayList<>();
		for (Filter filter : securityFilterChain.getFilters()) {
			if (filter instanceof FilterSecurityInterceptor) {
				DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
						(FilterSecurityInterceptor) filter);
				privilegeEvaluators.add(defaultWebInvocationPrivilegeEvaluator);
				continue;
			}
			if (filter instanceof AuthorizationFilter) {
				AuthorizationManager<HttpServletRequest> authorizationManager = ((AuthorizationFilter) filter)
						.getAuthorizationManager();
				AuthorizationManagerWebInvocationPrivilegeEvaluator evaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
						authorizationManager);
				privilegeEvaluators.add(evaluator);
			}
		}
		return new RequestMatcherEntry<>(securityFilterChain::matches, privilegeEvaluators);//2 match --> privilegeEvaluators
	}
}

3.3.2.1 关键接口: WebInvocationPrivilegeEvaluator

java 复制代码
public interface WebInvocationPrivilegeEvaluator {
    boolean isAllowed(String uri, Authentication authentication);

    boolean isAllowed(String contextPath, String uri, String method, Authentication authentication);
}

类图

3.3.2.2 关键类:RequestMatcherDelegatingWebInvocationPrivilegeEvaluator

java 复制代码
public final class RequestMatcherDelegatingWebInvocationPrivilegeEvaluator
		implements WebInvocationPrivilegeEvaluator, ServletContextAware {

	//构造函数 set
	private final List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> delegates;

	
	@Override
	public boolean isAllowed(String uri, Authentication authentication) {
		List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = getDelegate(null, uri, null);
		//for-each privilegeEvaluators .isAllowed(uri, authentication) 略...
		return true;
	}


	@Override
	public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
		List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = getDelegate(contextPath, uri, method);
		//for-each privilegeEvaluators .isAllowed(contextPath, uri, method, authentication); 略...
		return true;
	}

	private List<WebInvocationPrivilegeEvaluator> getDelegate(String contextPath, String uri, String method) {
		//filter 匹配的 WebInvocationPrivilegeEvaluator
		FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext);
		for (RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate : this.delegates) {
			if (delegate.getRequestMatcher().matches(filterInvocation.getHttpRequest())) {
				return delegate.getEntry();
			}
		}
		return Collections.emptyList();
	}
}

3.4总体处理流程(细)

3.4.1.FilterChainProxy.doFilter()

java 复制代码
public class FilterChainProxy extends GenericFilterBean {
	private List<SecurityFilterChain> filterChains;
    private HttpFirewall firewall;
    private RequestRejectedHandler requestRejectedHandler;
	
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
       try {
            request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
            this.doFilterInternal(request, response, chain); //1
       } catch (Exception var11) {
       		//略....
       		this.requestRejectedHandler.handle((HttpServletRequest)request, (HttpServletResponse)response, (RequestRejectedException)requestRejectedException);
        } finally {
            SecurityContextHolder.clearContext();
            request.removeAttribute(FILTER_APPLIED);
        }
    }
	
	private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request); //1
        HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);//2
        List<Filter> filters = this.getFilters((HttpServletRequest)firewallRequest);
    
		//遍历filterchain dofilter....
      	VirtualFilterChain virtualFilterChain = new 	VirtualFilterChain(firewallRequest, chain, filters);
        virtualFilterChain.doFilter(firewallRequest, firewallResponse);
    }
}

3.4.1.1关键接口: HttpFirewall

https://docs.spring.io/spring-security/reference/servlet/exploits/firewall.html

使用样例

java 复制代码
@Bean
public StrictHttpFirewall httpFirewall() {
    StrictHttpFirewall firewall = new StrictHttpFirewall();
    firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
    return firewall;
}

3.4.2.Filter.doFilter()

3.4.2.1 UsernamePasswordAuthenticationFilter

java 复制代码
UsernamePasswordAuthenticationFilter.doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
	AbstractAuthenticationProcessingFilter.doFilter(request,response,chain)
		doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			UsernamePasswordAuthenticationFilter.attemptAuthentication()


///
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
		implements ApplicationEventPublisherAware, MessageSourceAware {

	private AuthenticationManager authenticationManager;	

	//1.
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);//2.
	}

	//2.
	private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		if (!requiresAuthentication(request, response)) { //3.
			chain.doFilter(request, response);
			return;
		}
		Authentication authenticationResult = attemptAuthentication(request, response);//4 由子类实现
		if (authenticationResult == null) {
			return;
		}
		this.sessionStrategy.onAuthentication(authenticationResult, request, response);
		
		if (this.continueChainBeforeSuccessfulAuthentication) { //default false;
			chain.doFilter(request, response);
		}
		successfulAuthentication(request, response, chain, authenticationResult); //5
	}

	
	//3.
	protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
		if (this.requiresAuthenticationRequestMatcher.matches(request)) {
			return true;
		}
		return false;
	}


	//4
	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
			Authentication authResult) throws IOException, ServletException {
		SecurityContext context = SecurityContextHolder.createEmptyContext();
		context.setAuthentication(authResult);
		SecurityContextHolder.setContext(context);
		this.securityContextRepository.saveContext(context, request, response);
		
		this.successHandler.onAuthenticationSuccess(request, response, authResult);
	}
}

/
//4.
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", "POST");
    private String usernameParameter = "username";
    private String passwordParameter = "password";
    private boolean postOnly = true;

    public UsernamePasswordAuthenticationFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }

    public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
    }

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
            
            //request类型为:UsernamePasswordAuthenticationToken,且为unauthenticated
            UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
            this.setDetails(request, authRequest);
            //交由AuthenticationManager来进行认证: 
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

}

4.AuthenticationManager authentication(认证)

4.1 AuthenticationManager初始化

AuthenticationManager是在AuthenticationConfiguration中由@beanAuthenticationManagerBuilder.build()初始化。

AuthenticationManagerBuilder的具体类型为:DefaultPasswordEncoderAuthenticationManagerBuilder

同时AuthenticationManagerBuilder.apply了

  • EnableGlobalAuthenticationAutowiredConfigurer
  • InitializeUserDetailsBeanManagerConfigurer : 注入UserDetailsService
  • InitializeAuthenticationProviderBeanManagerConfigurer

4.1.1 关键配置

4.1.1.1 InitializeUserDetailsBeanManagerConfigurer

java 复制代码
class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {

	@Override
	public void init(AuthenticationManagerBuilder auth) throws Exception {
		auth.apply(new InitializeUserDetailsManagerConfigurer());
	}

	class InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {

		@Override
		public void configure(AuthenticationManagerBuilder auth) throws Exception {
			if (auth.isConfigured()) {
				return;
			}
			UserDetailsService userDetailsService = getBeanOrNull(UserDetailsService.class);
			if (userDetailsService == null) {
				return;
			}
			PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
			UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
			DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
			provider.setUserDetailsService(userDetailsService); //注入UserDetailsService
			if (passwordEncoder != null) {
				provider.setPasswordEncoder(passwordEncoder);
			}
			if (passwordManager != null) {
				provider.setUserDetailsPasswordService(passwordManager);
			}
			provider.afterPropertiesSet();
			auth.authenticationProvider(provider); //添加DaoAuthenticationProvider
		}
	}
}

4.1.1.2 InitializeUserDetailsBeanManagerConfigurer

java 复制代码
@Order(InitializeAuthenticationProviderBeanManagerConfigurer.DEFAULT_ORDER)
class InitializeAuthenticationProviderBeanManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {

	@Override
	public void init(AuthenticationManagerBuilder auth) throws Exception {
		auth.apply(new InitializeAuthenticationProviderManagerConfigurer());
	}

	class InitializeAuthenticationProviderManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {

		@Override
		public void configure(AuthenticationManagerBuilder auth) {
			if (auth.isConfigured()) {
				return;
			}
			AuthenticationProvider authenticationProvider = getBeanOrNull(AuthenticationProvider.class); //注入其他AuthenticationProvider
			if (authenticationProvider == null) {
				return;
			}
			auth.authenticationProvider(authenticationProvider);
		}
	}

}

4.1.2.DefaultPasswordEncoderAuthenticationManagerBuilder.build()

4.1.2.1. 类图

4.1.2.2.DefaultPasswordEncoderAuthenticationManagerBuilder.build()

java 复制代码
//1.执行流程
DefaultPasswordEncoderAuthenticationManagerBuilder.build()
	AbstractSecurityBuilder.build()
		AbstractConfiguredSecurityBuilder.doBuild()
			//beforeInit(); init(); beforeConfigure(); configure(); 略
			performBuild();
				AuthenticationManagerBuilder.performBuild()
//
public class AuthenticationManagerBuilder
		extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
		implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
			private AuthenticationManager parentAuthenticationManager;
			private List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
			private UserDetailsService defaultUserDetailsService;

	@Override
	protected ProviderManager performBuild() throws Exception {
		
		// AuthenticationManager具体类型为 ProviderManager
		ProviderManager providerManager = new ProviderManager(this.authenticationProviders,
				this.parentAuthenticationManager);
		providerManager = postProcess(providerManager);
		return providerManager;
	}
}

4.1.2.3 ProviderManager类图

4.3 ProviderManager.authenticate()

java 复制代码
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {

	private List<AuthenticationProvider> providers = Collections.emptyList();
	private AuthenticationManager parent;


	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		Authentication result = null;
		Authentication parentResult = null;

		for (AuthenticationProvider provider : getProviders()) { //获取AuthenticationProvider
			if (!provider.supports(toTest)) {
				continue;
			}
	
			result = provider.authenticate(authentication);
			if (result != null) {
				copyDetails(authentication, result);
				break;
			}
		}
		if (result == null && this.parent != null) {
			// Allow the parent to try.
			parentResult = this.parent.authenticate(authentication);
			result = parentResult;
			
		}
		if (result != null) {
			if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
				// Authentication is complete. Remove credentials and other secret data
				// from authentication
				((CredentialsContainer) result).eraseCredentials();
			}

			return result;
		}
		throw lastException;
	}
}

4.3.1 AuthenticationProvider

4.3.1.1 接口定义

java 复制代码
public interface AuthenticationProvider {
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
	boolean supports(Class<?> authentication);
}

4.3.1.2 类图

4.3.1.3 DaoAuthenticationProvider 示例

在前文有提到,在build AuthenticationMananger时,会注入一个DaoAuthenticationProvider

java 复制代码
public abstract class AbstractUserDetailsAuthenticationProvider
		implements AuthenticationProvider, InitializingBean, MessageSourceAware {

	 
	private UserCache userCache = new NullUserCache(); //可通过set设置
	private UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks(); //可通过set设置
	private UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks();//可通过set设置


	//仅支持UsernamePasswordAuthenticationToken类型的请求
	public boolean supports(Class<?> authentication) {
		return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
	}

	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		String username = determineUsername(authentication);
		UserDetails user = this.userCache.getUserFromCache(username); //优先从缓存获取
		if (user == null) {
			user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); //由子类实现 1
			Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
		}
		
		this.preAuthenticationChecks.check(user);
		additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); //子类实现 2
		this.postAuthenticationChecks.check(user);

		this.userCache.putUserInCache(user); //set 缓存
		
		Object principalToReturn = user //认证信息	
		return createSuccessAuthentication(principalToReturn, authentication, user); //子类实现3, 父类提供默认实现4
	}

	//4
	protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
			UserDetails user) {
		//getAuthorities: 可以理解为角色信息  或者权限码
		UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(principal,
				authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
		result.setDetails(authentication.getDetails());
		this.logger.debug("Authenticated user");
		return result;
	}
}

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

	private UserDetailsService userDetailsService;

	//1. 查询username信息
	protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
		if (loadedUser == null) {
			throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
		}
		return loadedUser;
	}

	//2. 验证密码
	protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
		if (authentication.getCredentials() == null) {
			throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
		}
		String presentedPassword = authentication.getCredentials().toString(); //登录时,输入的密码
		if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {//加密输入的密码 并于查询出来的密码做匹配验证
			throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
		}
	}

	//4.
	protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
			UserDetails user) {
		//默认为false:::: 自动更新密码???
		boolean upgradeEncoding = this.	 != null&& this.passwordEncoder.upgradeEncoding(user.getPassword());
		if (upgradeEncoding) {
			String presentedPassword = authentication.getCredentials().toString();
			String newPassword = this.passwordEncoder.encode(presentedPassword);
			user = this.userDetailsPasswordService.updatePassword(user, newPassword);
		}
		return super.createSuccessAuthentication(principal, authentication, user);
	}

}

4.3.2 Authentication

4.3.2.1. 接口定义

java 复制代码
public interface Authentication extends Principal, Serializable {
	//授权码 && 角色码 .... **后续鉴权流程会使用到**
	Collection<? extends GrantedAuthority> getAuthorities();

	Object getCredentials();
	Object getDetails();
	Object getPrincipal();
	boolean isAuthenticated();
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

4.3.2.2. UsernamePasswordAuthenticationToken 类图



5. authorization(鉴权)

https://docs.spring.io/spring-security/reference/5.7/servlet/authorization/index.html

5.1 架构

5.1.1. AuthorizationManager

5.1.1.1接口定义

java 复制代码
public interface AuthorizationManager<T> {
	//若没有权限,直接返回"Access Denied"
	default void verify(Supplier<Authentication> authentication, T object) {
		AuthorizationDecision decision = check(authentication, object);
		if (decision != null && !decision.isGranted()) {
			throw new AccessDeniedException("Access Denied");
		}
	}
	@Nullable
	AuthorizationDecision check(Supplier<Authentication> authentication, T object);
}

5.1.1.2.类图

5.1.2 AccessDecisionManager

5.1.2.1 AccessDecisionVoter

java 复制代码
public interface AccessDecisionVoter<S> {
	int ACCESS_GRANTED = 1;
	int ACCESS_ABSTAIN = 0;
	int ACCESS_DENIED = -1;

	boolean supports(ConfigAttribute attribute);
	boolean supports(Class<?> clazz);

	//投票; 交由AccessDecisionManager统计结果,最终决定是否通过鉴权
	int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
}

5.1.2.1.AccessDecisionManager接口定义

java 复制代码
public interface AccessDecisionManager {
	//决定是否通过鉴权,如果未通过则抛出异常
	void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException, InsufficientAuthenticationException;
	boolean supports(ConfigAttribute attribute);
	boolean supports(Class<?> clazz);
}

5.1.2.2 类图

5.2. 鉴权

5.2.1. 通过AuthorizationFilter鉴权

5.2.1.1 流程图

5.2.1.2 http启用authorize

更多方式参照:https://docs.spring.io/spring-security/reference/5.7/servlet/authorization/authorize-http-requests.html

java 复制代码
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
    http ...
    .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated();) //1
    return http.build();
}


	public HttpSecurity authorizeHttpRequests(
			Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
			throws Exception {
		authorizeHttpRequestsCustomizer
				.customize(getOrApply(new AuthorizeHttpRequestsConfigurer<>(context)).getRegistry());
		return HttpSecurity.this;
	}

5.2.1.3 AuthorizeHttpRequestsConfigurer.configure()

java 复制代码
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {

	private final AuthorizationManagerRequestMatcherRegistry registry;

	public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
		this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
	}

	@Override
	public void configure(H http) {
		AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
		AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager); 
		authorizationFilter.setShouldFilterAllDispatcherTypes(this.registry.shouldFilterAllDispatcherTypes);
		http.addFilter(postProcess(authorizationFilter)); //2.注册 extends AbstractRequestMatcherRegistry<AuthorizedUrl> {
AuthorizationFilter
	}

	public final class AuthorizationManagerRequestMatcherRegistry extends AbstractRequestMatcherRegistry<AuthorizedUrl> {
			
		private final RequestMatcherDelegatingAuthorizationManager.Builder managerBuilder = RequestMatcherDelegatingAuthorizationManager.builder();
				
	
		private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
			return postProcess(this.managerBuilder.build());
		}
	}

}

5.2.2. 通过FilterSecurityInterceptor鉴权

https://docs.spring.io/spring-security/reference/5.7/servlet/authorization/authorize-requests.html

5.2.2.1 流程图

5.2.2.2常见注解

https://docs.spring.io/spring-security/reference/5.7/servlet/authorization/expression-based.html#el-common-built-in

  • hasRole(String role)
  • hasAnyRole(String... roles)
  • hasAuthority(String authority)
  • hasAnyAuthority(String... authorities)
  • isAuthenticated()
  • permitAll
  • denyAll
  • hasPermission(Object target, Object permission)
  • hasPermission(Object targetId, String targetType, Object permission)

5.2.2.3 配置

java 复制代码
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
	http
		// ...
		.authorizeRequests(authorize -> authorize                                  
			.mvcMatchers("/resources/**", "/signup", "/about").permitAll()         
			.mvcMatchers("/admin/**").hasRole("ADMIN")                             
			.mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")    //1.http.register类型为:ExpressionInterceptUrlRegistry
			.anyRequest().denyAll()                                                
		);
	return http.build();
}

ExpressionInterceptUrlRegistry.mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")

5.2.2.4 配置拆解1-ExpressionInterceptUrlRegistry.mvcMatchers()

java 复制代码
//see  HttpSecurity.authorizeRequests()
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {
	//1.
	public final class ExpressionInterceptUrlRegistry extends
			ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl> {

		public MvcMatchersAuthorizedUrl mvcMatchers(String... patterns) {
			return mvcMatchers(null, patterns);
		}

		public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
			return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));//2
		}
	}

	//2
	public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
		private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> requestMatchers) {
			super(requestMatchers);//3
		}
	}

	//3	
	public class AuthorizedUrl {
		private List<? extends RequestMatcher> requestMatchers;


		AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
			this.requestMatchers = requestMatchers;
		}
	}

}

返回对象为: MvcMatchersAuthorizedUrl

5.2.2.5 配置拆解2-MvcMatchersAuthorizedUrl.access()

java 复制代码
//see  HttpSecurity.authorizeRequests()
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {

	static final String permitAll = "permitAll";
	private static final String denyAll = "denyAll";
	private static final String anonymous = "anonymous";
	private static final String authenticated = "authenticated";
	private static final String fullyAuthenticated = "fullyAuthenticated";
	private static final String rememberMe = "rememberMe";
	private final String rolePrefix;
	private final ExpressionInterceptUrlRegistry REGISTRY;
	private SecurityExpressionHandler<FilterInvocation> expressionHandler;

	public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
		//set rolePrefix略,默认为为"ROLE_";
		this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
	}


	//2
	private void interceptUrl(Iterable<? extends RequestMatcher> requestMatchers,
			Collection<ConfigAttribute> configAttributes) {
		for (RequestMatcher requestMatcher : requestMatchers) {
			this.REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(requestMatcher, configAttributes));
		}
	}



	public class AuthorizedUrl {

		private List<? extends RequestMatcher> requestMatchers;

		private boolean not;

		public ExpressionInterceptUrlRegistry hasRole(String role) {
			return access(ExpressionUrlAuthorizationConfigurer
					.hasRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, role));
		}

	
		public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) {
			return access(ExpressionUrlAuthorizationConfigurer
					.hasAnyRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, roles));
		}

		public ExpressionInterceptUrlRegistry hasAuthority(String authority) {
			return access(ExpressionUrlAuthorizationConfigurer.hasAuthority(authority));
		}

		public ExpressionInterceptUrlRegistry permitAll() {
			return access(permitAll);
		}

		
		public ExpressionInterceptUrlRegistry authenticated() {
			return access(authenticated);//1
		}


		public ExpressionInterceptUrlRegistry access(String attribute) {
			if (this.not) {
				attribute = "!" + attribute;
			}
			//string -> SecurityConfig(ConfigAttribute)
			interceptUrl(this.requestMatchers, SecurityConfig.createList(attribute)); 
			return ExpressionUrlAuthorizationConfigurer.this.REGISTRY;
		}

	}

}

5.2.2.6 ExpressionUrlAuthorizationConfigurer类图

5.2.2.7 ExpressionUrlAuthorizationConfigurer.configure()

java 复制代码
public abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>
		extends AbstractHttpConfigurer<C, H> {

	private AccessDecisionManager accessDecisionManager;


	@Override
	public void configure(H http) throws Exception {
		FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http); //1. 子类实现
	
		FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(http, metadataSource,http.getSharedObject(AuthenticationManager.class)); //2
		securityInterceptor = postProcess(securityInterceptor);
		http.addFilter(securityInterceptor);
		http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
	}


	//2
	private FilterSecurityInterceptor createFilterSecurityInterceptor(H http,
			FilterInvocationSecurityMetadataSource metadataSource, AuthenticationManager authenticationManager)
			throws Exception {
		FilterSecurityInterceptor securityInterceptor = new FilterSecurityInterceptor();
		securityInterceptor.setSecurityMetadataSource(metadataSource);

		//默认为:this.createDefaultAccessDecisionManager()//3
		securityInterceptor.setAccessDecisionManager(getAccessDecisionManager(http));
		securityInterceptor.setAuthenticationManager(authenticationManager);
		securityInterceptor.afterPropertiesSet();
		return securityInterceptor;
	}


	//3
	private AccessDecisionManager createDefaultAccessDecisionManager(H http) {
		AffirmativeBased result = new AffirmativeBased(getDecisionVoters(http));//4. 由子类实现
		return postProcess(result);
	}
}

/子类
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {

	private final ExpressionInterceptUrlRegistry REGISTRY;
	private SecurityExpressionHandler<FilterInvocation> expressionHandler;

	public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
		this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
	}

	private void interceptUrl(Iterable<? extends RequestMatcher> requestMatchers,
			Collection<ConfigAttribute> configAttributes) {
		for (RequestMatcher requestMatcher : requestMatchers) {
			this.REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(requestMatcher, configAttributes));
		}
	}

	
	//4
	@Override
	List<AccessDecisionVoter<?>> getDecisionVoters(H http) {
		List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
		WebExpressionVoter expressionVoter = new WebExpressionVoter();
		expressionVoter.setExpressionHandler(getExpressionHandler(http));//5.类型为DefaultWebSecurityExpressionHandler
		decisionVoters.add(expressionVoter);//类型为WebExpressionVoter
		return decisionVoters;
	}
	
	//1.
	@Override
	ExpressionBasedFilterInvocationSecurityMetadataSource createMetadataSource(H http) {
		LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = this.REGISTRY.createRequestMap();
		return new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap, getExpressionHandler(http));
	}

	//5
	private SecurityExpressionHandler<FilterInvocation> getExpressionHandler(H http) {
		
		DefaultWebSecurityExpressionHandler defaultHandler = new DefaultWebSecurityExpressionHandler();
		AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
		defaultHandler.setTrustResolver(trustResolver);
		ApplicationContext context = http.getSharedObject(ApplicationContext.class);
		String[] roleHiearchyBeanNames = context.getBeanNamesForType(RoleHierarchy.class);
		
		//设置RoleHierarchy 以及rolePrefix...
		defaultHandler.setRoleHierarchy(context.getBean(roleHiearchyBeanNames[0], RoleHierarchy.class));		
defaultHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
			
		//在GlobalMethodSecurityConfiguration中定义,略......
		String[] permissionEvaluatorBeanNames = context.getBeanNamesForType(PermissionEvaluator.class);
		PermissionEvaluator permissionEvaluator = context.getBean(permissionEvaluatorBeanNames[0],PermissionEvaluator.class);
		defaultHandler.setPermissionEvaluator(permissionEvaluator);

		this.expressionHandler = postProcess(defaultHandler);
		return this.expressionHandler;
	}

}		

5.2.2.8 重要接口: SecurityExpressionHandler

5.2.2.9 重要接口: PermissionEvaluator

java 复制代码
public interface PermissionEvaluator extends AopInfrastructureBean {

	boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission);

	boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);

}

5.2.2.10 AffirmativeBased.decide()

java 复制代码
public class AffirmativeBased extends AbstractAccessDecisionManager {

	public AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {
		super(decisionVoters);
	}

	@Override
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException {
		int deny = 0;
		for (AccessDecisionVoter voter : getDecisionVoters()) { //for-each 进行投票
			int result = voter.vote(authentication, object, configAttributes);
			switch (result) {
			case AccessDecisionVoter.ACCESS_GRANTED:
				return;
			case AccessDecisionVoter.ACCESS_DENIED:
				deny++;
				break;
			default:
				break;
			}
		}
		if (deny > 0) {
			throw new AccessDeniedException(this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
					
		}
		//到这里,所有人全部期权;
		checkAllowIfAllAbstainDecisions();
	}

}

5.2.2.11 WebExpressionVoter.vote()

java 复制代码
public class WebExpressionVoter implements AccessDecisionVoter<FilterInvocation> {
		private SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();

	public int vote(Authentication authentication, FilterInvocation filterInvocation,
			Collection<ConfigAttribute> attributes) {
		//attribute instanceof WebExpressionConfigAttribute
		WebExpressionConfigAttribute webExpressionConfigAttribute = findConfigAttribute(attributes);
		if (webExpressionConfigAttribute == null) {
			return ACCESS_ABSTAIN;
		}
		//2.webExpressionConfigAttribute.postProcess() 
		//返回:new VariableEvaluationContext(WebSecurityExpressionRoot.class, invocation.getHttpRequest())
		EvaluationContext ctx = webExpressionConfigAttribute.postProcess(
				this.expressionHandler.createEvaluationContext(authentication, filterInvocation), filterInvocation); //类型为:DefaultWebSecurityExpressionHandler1,返回值为WebSecurityExpressionRoot
	
		//3
		boolean granted = ExpressionUtils.evaluateAsBoolean(webExpressionConfigAttribute.getAuthorizeExpression(), ctx);
		if (granted) {
			return ACCESS_GRANTED;
		}
		return ACCESS_DENIED;
	}

	private WebExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {
		for (ConfigAttribute attribute : attributes) {
			if (attribute instanceof WebExpressionConfigAttribute) {
				return (WebExpressionConfigAttribute) attribute;
			}
		}
		return null;
	}
}

5.2.2.12.DefaultWebSecurityExpressionHandler.createSecurityExpressionRoot()

java 复制代码
public class DefaultWebSecurityExpressionHandler extends AbstractSecurityExpressionHandler<FilterInvocation>
		implements SecurityExpressionHandler<FilterInvocation> {
			
	private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
	private String defaultRolePrefix = "ROLE_";

	//在ExpressionUrlAuthorizationConfigurer#getExpressionHandler()中定义
	protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,FilterInvocation fi) {
		WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, fi);
		root.setPermissionEvaluator(getPermissionEvaluator());
		root.setTrustResolver(this.trustResolver);
		root.setRoleHierarchy(getRoleHierarchy());
		root.setDefaultRolePrefix(this.defaultRolePrefix);
		return root; //返回
	}
}

5.2.2.13 ExpressionUtils.evaluateAsBoolean(context,invocation)

java 复制代码
public final class ExpressionUtils {

	private ExpressionUtils() {
	}

	public static boolean evaluateAsBoolean(Expression expr, EvaluationContext ctx) {
		//ctx:new  VariableEvaluationContext(new WebSecurityExpressionRoot())`
		//expr: SpelExpression 调用SPEL表达式
		return expr.getValue(ctx, Boolean.class);
	}

}

5.2.2.14 重要接口-WebSecurityExpressionRoot

5.2.2.15 重要接口-VariableEvaluationContext(略)

5.2.3. 通过Expression鉴权

https://docs.spring.io/spring-security/reference/5.7/servlet/authorization/expression-based.html

5.2.3.2 Referring to Beans in Web Security Expressions

java 复制代码
//1.定义工具类
public class WebSecurityAuthz {
		public boolean check(Authentication authentication, HttpServletRequest request) {
				...
		}
}

//2. httpsecurity注册
http
    .authorizeHttpRequests(authorize -> authorize
        .antMatchers("/user/**").access("@WebSecurityAuthz.check(authentication,request)")
        ...
    )

5.2.3.3 Path Variables in Web Security Expressions

java 复制代码
public class webSecurityAuthz2 {
		public boolean checkUserId(Authentication authentication, int id) {
				...
		}
}

http
	.authorizeHttpRequests(authorize -> authorize
		.antMatchers("/user/{userId}/**").access("@webSecurityAuthz2.checkUserId(authentication,#userId)")
		...
	);

5.2.3.4 Method Security Expressions

启用global-method-security

xml 复制代码
<global-method-security pre-post-annotations="enabled"/>
java 复制代码
@PreAuthorize("hasRole('USER')")
public void create(Contact contact)

@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);

@PreAuthorize("#c.name == authentication.name")
public void doSomething(@P("c") Contact contact);


@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);

@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);

5.2.3.5 @PreFilter and @PostFilter

在使用@PostFilter注释时,Spring Security会遍历返回的集合或映射,并删除所有提供的表达式为false的元素。对于数组,将返回一个新的数组实例,其中包含筛选后的元素。

java 复制代码
@PreAuthorize("hasRole('USER')")
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();


6.methodSecurity 流程分析

添加链接描述

6.1.@EnableGlobalMethodSecurity

6.1.1 接口定义

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ GlobalMethodSecuritySelector.class }) //通常使用:@GlobalMethodSecurityConfiguration
@EnableGlobalAuthentication
@Configuration
public @interface EnableGlobalMethodSecurity {
	boolean prePostEnabled() default false;
	boolean securedEnabled() default false;
	boolean jsr250Enabled() default false;
}

6.1.2.GlobalMethodSecurityConfiguration

java 复制代码
public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInitializingSingleton, BeanFactoryAware {

	private DefaultMethodSecurityExpressionHandler defaultMethodExpressionHandler = new DefaultMethodSecurityExpressionHandler(); 
	private AuthenticationManager authenticationManager;
	private AuthenticationManagerBuilder auth;

	private MethodSecurityExpressionHandler expressionHandler;
	private MethodSecurityInterceptor methodSecurityInterceptor;
	
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public MethodSecurityMetadataSource methodSecurityMetadataSource() {
		List<MethodSecurityMetadataSource> sources = new ArrayList<>();
		ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(getExpressionHandler());
				
		MethodSecurityMetadataSource customMethodSecurityMetadataSource = customMethodSecurityMetadataSource();
		sources.add(customMethodSecurityMetadataSource);
		
		sources.add(new PrePostAnnotationSecurityMetadataSource(attributeFactory));
		sources.add(new SecuredAnnotationSecurityMetadataSource());
		sources.add(jsr250MethodSecurityMetadataSource);
		return new DelegatingMethodSecurityMetadataSource(sources);
	}
	
	@Bean
	public MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {
		this.methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor(): new MethodSecurityInterceptor();
		this.methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager()); //1.
		this.methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager());//2.
		this.methodSecurityInterceptor.setSecurityMetadataSource(methodSecurityMetadataSource);
		
		// runAsManager default null;
		return this.methodSecurityInterceptor;
	}
	

	@Override
	public void afterSingletonsInstantiated() {
		initializeMethodSecurityInterceptor(); //内部调用:methodSecurityInterceptor.setAuthenticationManager(authenticationManager())

		PermissionEvaluator permissionEvaluator = getSingleBeanOrNull(PermissionEvaluator.class);
		this.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluator);

		RoleHierarchy roleHierarchy = getSingleBeanOrNull(RoleHierarchy.class);
		this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
		AuthenticationTrustResolver trustResolver = getSingleBeanOrNull(AuthenticationTrustResolver.class);
		if (trustResolver != null) {
			this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
		}
		
		//设置rolePrefix 略...

		this.defaultMethodExpressionHandler = this.objectPostProcessor.postProcess(this.defaultMethodExpressionHandler);
	}


	protected AccessDecisionManager accessDecisionManager() {
		List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
		if (prePostEnabled()) {
			ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
			expressionAdvice.setExpressionHandler(getExpressionHandler());
			decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice)); // add PreInvocationAuthorizationAdviceVoter
		}
		
		RoleVoter roleVoter = new RoleVoter();
		GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
		if (grantedAuthorityDefaults != null) {
			roleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
		}
		decisionVoters.add(roleVoter);
		decisionVoters.add(new AuthenticatedVoter()); // add AuthenticatedVoter
		return new AffirmativeBased(decisionVoters);
	}
	
	//由子类覆盖,达到自定义MethodSecurityExpressionHandler的目的;
	protected MethodSecurityExpressionHandler createExpressionHandler() {
		return this.defaultMethodExpressionHandler;
	}

	protected final MethodSecurityExpressionHandler getExpressionHandler() {
		if (this.expressionHandler == null) {
			this.expressionHandler = createExpressionHandler();
		}
		return this.expressionHandler;
	}
}

6.2. 认证+鉴权流程

通过AOP,拦截带有特定注解的方法

  • @PreAuthorize
  • @PostAuthorize
  • @PreFilter
  • @PostFilter
    通过MethodSecurityInterceptor 拦截.

6.2.1.重要接口- MethodSecurityMetadataSource

6.2.1.1 类图

6.2.1.2 PrePostAnnotationSecurityMetadataSource

java 复制代码
public class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {

	@Override
	public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
		if (method.getDeclaringClass() == Object.class) {
			return Collections.emptyList();
		}
		PreFilter preFilter = findAnnotation(method, targetClass, PreFilter.class);
		PreAuthorize preAuthorize = findAnnotation(method, targetClass, PreAuthorize.class);
		PostFilter postFilter = findAnnotation(method, targetClass, PostFilter.class);
		PostAuthorize postAuthorize = findAnnotation(method, targetClass, PostAuthorize.class);
		
		
		ArrayList<ConfigAttribute> attrs = new ArrayList<>(2);
		PreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(preFilterAttribute,
				filterObject, preAuthorizeAttribute);
		attrs.add(pre);
		PostInvocationAttribute post = this.attributeFactory.createPostInvocationAttribute(postFilterAttribute,
				postAuthorizeAttribute);
		attrs.add(post);
		return attrs;
	}

}

6.2.2.重要接口:MethodInterceptor

6.2.2.1 类图

6.2.2.2.MethodSecurityInterceptor

java 复制代码
public class MethodSecurityInterceptor extends AbstractSecurityInterceptor implements MethodInterceptor {

	private MethodSecurityMetadataSource securityMetadataSource;

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		InterceptorStatusToken token = super.beforeInvocation(mi); //1.AbstractSecurityInterceptor.beforeInvocation()
		Object result;
		try {
			result = mi.proceed();
		}
		finally {
			super.finallyInvocation(token); //2.
		}
		return super.afterInvocation(token, result);//3.
	}

}

public abstract class AbstractSecurityInterceptor
		implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {

	private AccessDecisionManager accessDecisionManager;
	private AfterInvocationManager afterInvocationManager;
	private AuthenticationManager authenticationManager = new NoOpAuthenticationManager();
	private RunAsManager runAsManager = new NullRunAsManager();		


	protected InterceptorStatusToken beforeInvocation(Object object) {
		
		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
		
		Authentication authenticated = authenticateIfRequired();
		
		attemptAuthorization(object, attributes, authenticated);	//1.1	
		return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);

	}

	//1.1
	private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
			Authentication authenticated) {
		//1.1.1
		this.accessDecisionManager.decide(authenticated, object, attributes);
	}
}

accessDecisionManager.decide 逻辑前文已经有说明,这里不再赘述.



9.自定义拓展

9.1 自定义验证规则

9.1.1 实现的功能

java 复制代码
    @PreAuthorize("isHuaWeiLogin() || isAppleLoginAndHasPermissions('mobile:add'))")
    public void xxx(){}

9.1.2 configuration

java 复制代码
@Configuration
public class WbsGlobalSecurityConfig extends GlobalMethodSecurityConfiguration {


    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        PrivilegeMethodSecurityExpressionHandler expressionHandler = new PrivilegeMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(null);
        return expressionHandler;
    }

    public class PrivilegeMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler {
        @Override
        protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
            PrivilegeMethodSecurityExpressionRoot root = new PrivilegeMethodSecurityExpressionRoot(authentication);
            root.setPermissionEvaluator(this.getPermissionEvaluator());
            root.setRoleHierarchy(this.getRoleHierarchy());
            return root;
        }
    }

    public class PrivilegeMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

        @Getter @Setter
        private Object filterObject;

        @Getter @Setter
        private Object returnObject;
        private Object target;

        PrivilegeMethodSecurityExpressionRoot(Authentication authentication) {
            super(authentication);
        }

        void setThis(Object target) {
            this.target = target;
        }

        public Object getThis() {
            return this.target;
        }

        private Map<String, Object> orgHelperMocked = Collections.emptyMap();
        public boolean isAppleLogin() {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication instanceof AppleUserAuthenticationToken userToken) {
                return userToken.getPrincipal() != null && userToken.isAuthenticated();
            } else {
                return false;
            }
        }

        public boolean isHuaWeiLogin() {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication instanceof HuaWeiUserAuthenticationToken userToken) {
                return userToken.getPrincipal() != null && userToken.isAuthenticated();
            } else {
                return false;
            }
        }

        public boolean isAppleLoginAndHasPermissions(String ...permission) {
            if(!isAppleLogin()){
                return false;
            }
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            //todo xxxx
            return false;
        }
    }
}
相关推荐
sg_knight2 小时前
Ribbon负载均衡实战指南:7种策略选择与生产避坑
java·spring boot·spring·spring cloud·微服务·ribbon·负载均衡
书语时4 小时前
Spring @Autowired解析
java·后端·spring
迢迢星万里灬6 小时前
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
java·spring boot·spring·mybatis·spring mvc·面试指南
面朝大海,春不暖,花不开7 小时前
Spring AI与Spring Modulith核心技术解析
人工智能·spring·flask
有梦想的攻城狮8 小时前
spring中的ImportSelector接口详解
java·后端·spring·接口·importselector
LUCIAZZZ9 小时前
Java设计模式基础问答
java·开发语言·jvm·spring boot·spring·设计模式
KotlinKUG贵州10 小时前
Spring开发,从Kotlin开始
spring boot·spring·kotlin
攒了一袋星辰11 小时前
Spring类型转换融入IOC生命周期
java·后端·spring
考虑考虑12 小时前
Springboot3.4.x中的RestClient 和 RestTemplate
spring boot·后端·spring
大白爱琴12 小时前
八股文——JVM
java·jvm·spring