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;
        }
    }
}
相关推荐
Ling_suu2 小时前
Spring——单元测试
java·spring·单元测试
代码小鑫3 小时前
A032-基于Spring Boot的健康医院门诊在线挂号系统
java·开发语言·spring boot·后端·spring·毕业设计
喔喔咿哈哈3 小时前
【手撕 Spring】 -- Bean 的创建以及获取
java·后端·spring·面试·开源·github
码农小丘3 小时前
了解springboot国际化用途以及使用
java·spring boot·spring
tian-ming3 小时前
JavaWeb后端开发知识储备1
java·spring boot·nginx·spring·maven
夏微凉.3 小时前
【JavaEE进阶】Spring AOP 原理
java·spring boot·后端·spring·java-ee·maven
杨过姑父3 小时前
org.springframework.context.support.ApplicationListenerDetector 详细介绍
java·前端·spring
理想不理想v4 小时前
使用JS实现文件流转换excel?
java·前端·javascript·css·vue.js·spring·面试
抱走江江4 小时前
SpringCloud框架学习(第二部分:Consul、LoadBalancer和openFeign)
学习·spring·spring cloud
不会编程的懒洋洋5 小时前
Spring Cloud Eureka 服务注册与发现
java·笔记·后端·学习·spring·spring cloud·eureka