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
- @EnableWebMvcSecurity --
@deprecated
- @EnableWebSecurity
- import:
WebSecurityConfiguration
- import:
HttpSecurityConfiguration
- import: SpringWebMvcImportSelector ===>
WebMvcSecurityConfiguration
(略) - import:
OAuth2ImportSelector
(略) @EnableGlobalAuthentication(4)
- import:
- @EnableGlobalMethodSecurity
@EnableGlobalAuthentication(4)
- import: GlobalMethodSecuritySelector ===>
GlobalMethodSecurityConfiguration
- @EnableGlobalAuthentication
- import:
AuthenticationConfiguration
- import:
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
: 注入UserDetailsServiceInitializeAuthenticationProviderBeanManagerConfigurer
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
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常见注解
- 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;
}
}
}