4.1 过滤链源码分析

4.1.1 过滤器链加载流程分析

4.1.2 过滤器链加载流程源码分析

  1. SpringBoot的自动配置会加载spring.factories文件,在文件中有针对Spring Security的过滤器链 的配置信息org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,表明过滤器链的自动配置类是SecurityFilterAutoConfiguration类。
  2. SecurityFilterAutoConfiguration类 以及相关导入的类 ```java @Configuration( proxyBeanMethods = false ) @ConditionalOnWebApplication( type = Type.SERVLET ) @EnableConfigurationProperties({SecurityProperties.class}) @ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class}) // 在SecurityFilterAutoConfiguration类加载完成后加载SecurityAutoConfiguration类 @AutoConfigureAfter({SecurityAutoConfiguration.class}) public class SecurityFilterAutoConfiguration {

}

  1. ```java
  2. @Configuration(
  3. proxyBeanMethods = false
  4. )
  5. @ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
  6. @EnableConfigurationProperties({SecurityProperties.class}) // 定义默认的用户名和密码
  7. @Import({SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class})
  8. public class SecurityAutoConfiguration {
  9. public SecurityAutoConfiguration() {
  10. }
  11. @Bean
  12. @ConditionalOnMissingBean({AuthenticationEventPublisher.class})
  13. public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
  14. return new DefaultAuthenticationEventPublisher(publisher);
  15. }
  16. }

SecurityProperties类:定义默认的用户名和密码

  1. @ConfigurationProperties(
  2. prefix = "spring.security"
  3. )
  4. public class SecurityProperties {
  5. public static final int BASIC_AUTH_ORDER = 2147483642;
  6. public static final int IGNORED_ORDER = -2147483648;
  7. public static final int DEFAULT_FILTER_ORDER = -100;
  8. private final SecurityProperties.Filter filter = new SecurityProperties.Filter();
  9. private final SecurityProperties.User user = new SecurityProperties.User();
  10. public SecurityProperties() {
  11. }
  12. public SecurityProperties.User getUser() {
  13. return this.user;
  14. }
  15. public SecurityProperties.Filter getFilter() {
  16. return this.filter;
  17. }
  18. // 定义了自动配置的user,也就是未连接数据库我们启动时可以使用登录的用户密码
  19. public static class User {
  20. private String name = "user";
  21. private String password = UUID.randomUUID().toString();
  22. private List<String> roles = new ArrayList();
  23. private boolean passwordGenerated = true;
  24. public User() {
  25. }
  26. public String getName() {
  27. return this.name;
  28. }
  29. public void setName(String name) {
  30. this.name = name;
  31. }
  32. public String getPassword() {
  33. return this.password;
  34. }
  35. public void setPassword(String password) {
  36. if (StringUtils.hasLength(password)) {
  37. this.passwordGenerated = false;
  38. this.password = password;
  39. }
  40. }
  41. public List<String> getRoles() {
  42. return this.roles;
  43. }
  44. public void setRoles(List<String> roles) {
  45. this.roles = new ArrayList(roles);
  46. }
  47. public boolean isPasswordGenerated() {
  48. return this.passwordGenerated;
  49. }
  50. }
  51. public static class Filter {
  52. private int order = -100;
  53. private Set<DispatcherType> dispatcherTypes;
  54. public Filter() {
  55. this.dispatcherTypes = new HashSet(Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
  56. }
  57. public int getOrder() {
  58. return this.order;
  59. }
  60. public void setOrder(int order) {
  61. this.order = order;
  62. }
  63. public Set<DispatcherType> getDispatcherTypes() {
  64. return this.dispatcherTypes;
  65. }
  66. public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) {
  67. this.dispatcherTypes = dispatcherTypes;
  68. }
  69. }
  70. }

WebSecurityEnablerConfiguration类:开启web安全启动配置

  1. @Configuration(
  2. proxyBeanMethods = false
  3. )
  4. @ConditionalOnMissingBean(
  5. name = {"springSecurityFilterChain"}
  6. )
  7. @ConditionalOnClass({EnableWebSecurity.class})
  8. @ConditionalOnWebApplication(
  9. type = Type.SERVLET
  10. )
  11. @EnableWebSecurity // 开启Security安全功能
  12. class WebSecurityEnablerConfiguration {
  13. WebSecurityEnablerConfiguration() {
  14. }
  15. }

EnableWebSecurity注解类:开启security安全功能

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target({ElementType.TYPE})
  3. @Documented
  4. @Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class})
  5. @EnableGlobalAuthentication
  6. @Configuration
  7. public @interface EnableWebSecurity {
  8. boolean debug() default false;
  9. }

WebSecurityConfiguration类:安全配置类,生成过滤器链

  1. @Configuration(
  2. proxyBeanMethods = false
  3. )
  4. public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
  5. // 此方法返回的就是过滤器链
  6. @Bean(
  7. name = {"springSecurityFilterChain"}
  8. )
  9. public Filter springSecurityFilterChain() throws Exception {
  10. boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
  11. boolean hasFilterChain = !this.securityFilterChains.isEmpty();
  12. Assert.state(!hasConfigurers || !hasFilterChain, "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
  13. if (!hasConfigurers && !hasFilterChain) {
  14. WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
  15. });
  16. this.webSecurity.apply(adapter);
  17. }
  18. Iterator var7 = this.securityFilterChains.iterator();
  19. while(true) {
  20. while(var7.hasNext()) {
  21. SecurityFilterChain securityFilterChain = (SecurityFilterChain)var7.next();
  22. this.webSecurity.addSecurityFilterChainBuilder(() -> {
  23. return securityFilterChain;
  24. });
  25. Iterator var5 = securityFilterChain.getFilters().iterator();
  26. while(var5.hasNext()) {
  27. Filter filter = (Filter)var5.next();
  28. if (filter instanceof FilterSecurityInterceptor) {
  29. this.webSecurity.securityInterceptor((FilterSecurityInterceptor)filter);
  30. break;
  31. }
  32. }
  33. }
  34. var7 = this.webSecurityCustomizers.iterator();
  35. while(var7.hasNext()) {
  36. WebSecurityCustomizer customizer = (WebSecurityCustomizer)var7.next();
  37. customizer.customize(this.webSecurity);
  38. }
  39. return (Filter)this.webSecurity.build();
  40. }
  41. }
  42. }

生成过滤器链的过程,就是会读取我们自己编写的配置类,设置相关的过滤器信息到configure上,还有一些默认的过滤器也设置到configure,然后对configure进行一个个初始化,生成相应的过滤器类,最后对生成的过滤器进行排序返回。

4.2 认证源码分析

4.2.1 认证流程分析

4. Spring Security源码分析 - 图1

4.2.2 认证流程源码跟踪

  • UsernamePasswordAuthenticationFilter

    1. public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    2. public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    3. // 先判断是不是POST请求,不是则抛出异常
    4. if (this.postOnly && !request.getMethod().equals("POST")) {
    5. throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
    6. } else {
    7. // 从request请求中通过配置的参数获取用户名密码
    8. String username = this.obtainUsername(request);
    9. username = username != null ? username : "";
    10. username = username.trim();
    11. String password = this.obtainPassword(request);
    12. password = password != null ? password : "";
    13. // 创建UsernamePasswordAuthenticationToken
    14. UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
    15. this.setDetails(request, authRequest);
    16. // 调用认证管理器的认证方法
    17. // 验证完成后就到父类的doFilter()方法,负责把认证信息放入SercurityContext
    18. // 然后调用SuceessHandler来处理登录成功逻辑
    19. return this.getAuthenticationManager().authenticate(authRequest);
    20. }
    21. }
    22. }
  • ProviderManager

    1. public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
    2. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    3. Class<? extends Authentication> toTest = authentication.getClass();
    4. AuthenticationException lastException = null;
    5. AuthenticationException parentException = null;
    6. Authentication result = null;
    7. Authentication parentResult = null;
    8. int currentPosition = 0;
    9. int size = this.providers.size();
    10. // 获取providers,发现两个都不支持解析UsernamePasswordAuthenticationToken
    11. Iterator var9 = this.getProviders().iterator();
    12. while(var9.hasNext()) {
    13. AuthenticationProvider provider = (AuthenticationProvider)var9.next();
    14. // 判断支不支持
    15. if (provider.supports(toTest)) {
    16. if (logger.isTraceEnabled()) {
    17. Log var10000 = logger;
    18. String var10002 = provider.getClass().getSimpleName();
    19. ++currentPosition;
    20. var10000.trace(LogMessage.format("Authenticating request with %s (%d/%d)", var10002, currentPosition, size));
    21. }
    22. try {
    23. // 验证方法
    24. result = provider.authenticate(authentication);
    25. if (result != null) {
    26. this.copyDetails(authentication, result);
    27. break;
    28. }
    29. } catch (InternalAuthenticationServiceException | AccountStatusException var14) {
    30. this.prepareException(var14, authentication);
    31. throw var14;
    32. } catch (AuthenticationException var15) {
    33. lastException = var15;
    34. }
    35. }
    36. }
    37. if (result == null && this.parent != null) {
    38. try {
    39. // 判断两个认证器都不支持,所以交给父类去验证
    40. // 父类同样是执行这个方法,发现Providers是DaoAuthenticationProvider
    41. // provider.supports(toTest)判断时是
    42. // 支持UsernamePasswordAuthenticationToken的
    43. parentResult = this.parent.authenticate(authentication);
    44. result = parentResult;
    45. } catch (ProviderNotFoundException var12) {
    46. } catch (AuthenticationException var13) {
    47. parentException = var13;
    48. lastException = var13;
    49. }
    50. }
    51. if (result != null) {
    52. if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) {
    53. ((CredentialsContainer)result).eraseCredentials();
    54. }
    55. if (parentResult == null) {
    56. this.eventPublisher.publishAuthenticationSuccess(result);
    57. }
    58. return result;
    59. } else {
    60. if (lastException == null) {
    61. lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[]{toTest.getName()}, "No AuthenticationProvider found for {0}"));
    62. }
    63. if (parentException == null) {
    64. this.prepareException((AuthenticationException)lastException, authentication);
    65. }
    66. throw lastException;
    67. }
    68. }
    69. }
  • AbstractUserDetailsAuthenticationProvider: ProviderManager中调用的的result = provider.authenticate(authentication)方法

    1. public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
    2. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    3. Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> {
    4. return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported");
    5. });
    6. // 获取用户名
    7. String username = this.determineUsername(authentication);
    8. boolean cacheWasUsed = true;
    9. // 通过用户名查询缓存中的user信息
    10. UserDetails user = this.userCache.getUserFromCache(username);
    11. if (user == null) {
    12. cacheWasUsed = false;
    13. try {
    14. // 检查user信息
    15. user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
    16. } catch (UsernameNotFoundException var6) {
    17. this.logger.debug("Failed to find user '" + username + "'");
    18. if (!this.hideUserNotFoundExceptions) {
    19. throw var6;
    20. }
    21. throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
    22. }
    23. Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
    24. }
    25. try {
    26. // 检查用户是否锁定、启用等等
    27. this.preAuthenticationChecks.check(user);
    28. // 检查用户是否合法,比对参数:user是数据库查询的,authentication是前端传的
    29. this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
    30. } catch (AuthenticationException var7) {
    31. if (!cacheWasUsed) {
    32. throw var7;
    33. }
    34. cacheWasUsed = false;
    35. user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
    36. this.preAuthenticationChecks.check(user);
    37. this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
    38. }
    39. this.postAuthenticationChecks.check(user);
    40. if (!cacheWasUsed) {
    41. // 检查无误则把user放入缓存
    42. this.userCache.putUserInCache(user);
    43. }
    44. Object principalToReturn = user;
    45. if (this.forcePrincipalAsString) {
    46. principalToReturn = user.getUsername();
    47. }
    48. // 创建新的UsernamePasswordAuthenticationToken,已认证的,有授权信息的
    49. return this.createSuccessAuthentication(principalToReturn, authentication, user);
    50. }
    51. }
  • DaoAuthenticationProvider: 上一步代码调用的检查user方法user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication)

    1. public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    2. protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    3. this.prepareTimingAttackProtection();
    4. try {
    5. // 调用我们自定义的MyUserDetailsService来获取UserDetails然后返回
    6. UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
    7. if (loadedUser == null) {
    8. throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
    9. } else {
    10. return loadedUser;
    11. }
    12. } catch (UsernameNotFoundException var4) {
    13. this.mitigateAgainstTimingAttack(authentication);
    14. throw var4;
    15. } catch (InternalAuthenticationServiceException var5) {
    16. throw var5;
    17. } catch (Exception var6) {
    18. throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
    19. }
    20. }
    21. // 上一步代码调用的
    22. protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    23. if (authentication.getCredentials() == null) {
    24. this.logger.debug("Failed to authenticate since no credentials provided");
    25. throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
    26. } else {
    27. // 获取密码并比对数据库与前端的密码
    28. String presentedPassword = authentication.getCredentials().toString();
    29. if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
    30. this.logger.debug("Failed to authenticate since password does not match stored value");
    31. throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
    32. }
    33. }
    34. }
    35. }

4.2.3 总结

  1. 调用UsernamePasswordAuthenticationFilter类,从request中通过配置的参数取出账号密码,然后用账号密码生成UsernamePasswordAuthenticationToken,此时的token是未认证的。
  2. 调用认证管理器ProviderManager的认证方法authenticate(Authentication authentication)进行认证。
  • 先匹配provider,通过比对发现DaoAuthenticationProvider支持UsernamePasswordAuthenticationToken的解析,然后调用provider.authenticate(authentication)进行验证。
  1. 验证其实是调用AbstractUserDetailsAuthenticationProvider中的authenticate(authentication)方法
  • 获取用户名,查询user有没有缓存,没有则调用this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication)进行user检查。
  • 对user进行前置检查,主要检查用户是否锁定,是否启用等等。
  • this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication)检查用户是否合法,两个参数分别数据库查询的user和前端传的认证信息,来进行密码的比对。
  • 检查无误把user放入缓存。
  • 创建新的UsernamePasswordAuthenticationToken,已认证的,有授权信息的。
  1. 上一步的this.retrieveUserthis.additionalAuthenticationChecks都是在DaoAuthenticationProvider类中实现的。
  • this.retrieveUser:调用我们自定义的UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username),得到userDetails信息并返回。
  • this.additionalAuthenticationChecks:获取密码并比对前端与数据库的密码是否一致。
  1. 验证完成后就到UsernamePasswordAuthenticationFilter父类的doFilter()方法,负责把认证信息放入SercurityContext,以便后续我们可以通过SercurityContext获取认证信息,然后调用SuceessHandler来处理登录成功逻辑。

4.3 rememberMe源码分析

4.4 Csrf源码分析

4.4.1 csrf流程分析

4. Spring Security源码分析 - 图2

4.4.2 csrf源码跟踪

  • CsrfFilter

    1. public final class CsrfFilter extends OncePerRequestFilter {
    2. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    3. request.setAttribute(HttpServletResponse.class.getName(), response);
    4. CsrfToken csrfToken = this.tokenRepository.loadToken(request);
    5. boolean missingToken = csrfToken == null;
    6. if (missingToken) {
    7. // 生成token并封装,token值就是通过UUID生成
    8. csrfToken = this.tokenRepository.generateToken(request);
    9. this.tokenRepository.saveToken(csrfToken, request, response);
    10. }
    11. // 把token放入request请求域中
    12. request.setAttribute(CsrfToken.class.getName(), csrfToken);
    13. request.setAttribute(csrfToken.getParameterName(), csrfToken);
    14. if (!this.requireCsrfProtectionMatcher.matches(request)) {
    15. if (this.logger.isTraceEnabled()) {
    16. this.logger.trace("Did not protect against CSRF since request did not match " + this.requireCsrfProtectionMatcher);
    17. }
    18. // 执行doFilter放行,放行到页面时要取出csrfToken的值,其实就是执行getToken方法
    19. // 其实就是执行SaveOnAccessCsrfToken的getToken方法
    20. filterChain.doFilter(request, response);
    21. } else {
    22. String actualToken = request.getHeader(csrfToken.getHeaderName());
    23. if (actualToken == null) {
    24. actualToken = request.getParameter(csrfToken.getParameterName());
    25. }
    26. if (!equalsConstantTime(csrfToken.getToken(), actualToken)) {
    27. this.logger.debug(LogMessage.of(() -> {
    28. return "Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request);
    29. }));
    30. AccessDeniedException exception = !missingToken ? new InvalidCsrfTokenException(csrfToken, actualToken) : new MissingCsrfTokenException(actualToken);
    31. this.accessDeniedHandler.handle(request, response, (AccessDeniedException)exception);
    32. } else {
    33. filterChain.doFilter(request, response);
    34. }
    35. }
    36. }
    37. }
  • LazyCsrfTokenRepository

    1. public final class LazyCsrfTokenRepository implements CsrfTokenRepository {
    2. private static final class SaveOnAccessCsrfToken implements CsrfToken {
    3. public String getToken() {
    4. // 把token存到session当中
    5. this.saveTokenIfNecessary();
    6. // 获取token返回
    7. return this.delegate.getToken();
    8. }
    9. }
    10. }

第一次请求总结:第一次请求页面的时候,会通过CsrfFilter来拦截请求,生成token并且封装放入request请求域中(token值是通过UUID生成),然后页面通过${_csrf.token}取值的时候,其实就是调用SaveOnAccessCsrfTokengetToken方法,将token保存到session域中,然后返回token值,这样,我们的页面上就有csrfToken的值了,下次请求就会带上这个token值。

第二次请求总结:this.tokenRepository.loadToken(request)可以从session中获取到csrfToken的值并返回,然后通过页面传过来的token值与session域中的token值进行比对,然后放行。

4.5 授权源码分析

在整个过滤器链中, FilterSecurityInterceptor是来处理整个用户授权流程的, 也是距离用户API最后一
个非常重要的过滤器链。

4.5.1 授权流程分析

4. Spring Security源码分析 - 图3

  • AffirmativeBased(基于肯定)的逻辑是: 一票通过权
  • ConsensusBased(基于共识)的逻辑是: 赞成票多于反对票则表示通过,反对票多于赞成票则将抛出 AccessDeniedException
  • UnanimousBased(基于一致)的逻辑:一票否决权

总结:FilterSecurityInterceptor类通过读取系统配置,获得了用户权限,然后交给AccessDecisionManager类去调用决策器AccessDecisionVoter进行权限认证,决策策略如上三种所示,决策通过则可以调用API,决策不通过则抛出异常,可以被异常过滤器ExceptionTranslationFilter捕获。

4.5.2 授权源码跟踪

  • FilterSecurityInterceptor

    1. public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    2. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    3. this.invoke(new FilterInvocation(request, response, chain));
    4. }
    5. public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
    6. if (this.isApplied(filterInvocation) && this.observeOncePerRequest) {
    7. filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
    8. } else {
    9. if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
    10. filterInvocation.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
    11. }
    12. // 前置处理
    13. InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
    14. try {
    15. filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
    16. } finally {
    17. super.finallyInvocation(token);
    18. }
    19. super.afterInvocation(token, (Object)null);
    20. }
    21. }
    22. }
  • AbstractSecurityInterceptor

    1. public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {
    2. protected InterceptorStatusToken beforeInvocation(Object object) {
    3. Assert.notNull(object, "Object was null");
    4. if (!this.getSecureObjectClass().isAssignableFrom(object.getClass())) {
    5. throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName() + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + this.getSecureObjectClass());
    6. } else {
    7. // 获取我们在配置类中编写的权限列表(antMatch的配置)
    8. // getAttributes()通过遍历权限列表,匹配到与当前请求url相同的权限,并返回
    9. Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
    10. if (CollectionUtils.isEmpty(attributes)) {
    11. Assert.isTrue(!this.rejectPublicInvocations, () -> {
    12. return "Secure object invocation " + object + " was denied as public invocations are not allowed via this interceptor. This indicates a configuration error because the rejectPublicInvocations property is set to 'true'";
    13. });
    14. if (this.logger.isDebugEnabled()) {
    15. this.logger.debug(LogMessage.format("Authorized public object %s", object));
    16. }
    17. this.publishEvent(new PublicInvocationEvent(object));
    18. return null;
    19. } else {
    20. if (SecurityContextHolder.getContext().getAuthentication() == null) {
    21. this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
    22. }
    23. // 从SecurityContext上下文中获取用户的所有权限信息
    24. Authentication authenticated = this.authenticateIfRequired();
    25. if (this.logger.isTraceEnabled()) {
    26. this.logger.trace(LogMessage.format("Authorizing %s with attributes %s", object, attributes));
    27. }
    28. // 通过this.accessDecisionManager.decide(authenticated, object, attributes)调用决策器去决定权限是否通过,默认是AffirmativeBased(一票通过),投票的返回值0表示弃权,1表示通过,-1表示不通过,通过则返回放行
    29. this.attemptAuthorization(object, attributes, authenticated);
    30. if (this.logger.isDebugEnabled()) {
    31. this.logger.debug(LogMessage.format("Authorized %s with attributes %s", object, attributes));
    32. }
    33. if (this.publishAuthorizationSuccess) {
    34. this.publishEvent(new AuthorizedEvent(object, attributes, authenticated));
    35. }
    36. Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
    37. if (runAs != null) {
    38. SecurityContext origCtx = SecurityContextHolder.getContext();
    39. SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
    40. SecurityContextHolder.getContext().setAuthentication(runAs);
    41. if (this.logger.isDebugEnabled()) {
    42. this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs));
    43. }
    44. return new InterceptorStatusToken(origCtx, true, attributes, object);
    45. } else {
    46. this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null");
    47. return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
    48. }
    49. }
    50. }
    51. }
    52. }