JAVA——springSecurity——底层原理分析:处理认证请求和非认证请求的流程,主要过滤器链的作用

一、认证请求(login)

1.spring管理过滤器的生命周期

在加载时启动一个叫DelegatingFilterProxy的过滤器代理对象,让spring管理这些过滤器的生命周期

DelegatingFilterProxy类——doFilter()

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
    
    //启动一个叫DelegatingFilterProxy的过滤器代理对象
    Filter delegateToUse = this.delegate;
    if (delegateToUse == null) {
    
    
        synchronized(this.delegateMonitor) {
    
    
            delegateToUse = this.delegate;
            if (delegateToUse == null) {
    
    
                WebApplicationContext wac = this.findWebApplicationContext();
                if (wac == null) {
    
    
                    throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
                }

                delegateToUse = this.initDelegate(wac);
            }

            this.delegate = delegateToUse;
        }
    }

    this.invokeDelegate(delegateToUse, request, response, filterChain);
}

2.初始化一个FilterChainProxy过滤器链代理对象

初始化一个过滤链FilterChainProxy过滤器链的代理对象,在这个过滤器链代理对象中有一个过滤器链集合,每一个过滤器链都有一组过滤器来处理不同的请求

FilterChainProxy类——doFilter()

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
    
    
    if (this.currentPosition == this.size) {
    
    
        if (FilterChainProxy.logger.isDebugEnabled()) {
    
    
            FilterChainProxy.logger.debug(UrlUtils.buildRequestUrl(this.firewalledRequest) + " reached end of additional filter chain; proceeding with original chain");
        }

        this.firewalledRequest.reset();
        this.originalChain.doFilter(request, response);
    } else {
    
    
        ++this.currentPosition;
        Filter nextFilter = (Filter)this.additionalFilters.get(this.currentPosition - 1);
        if (FilterChainProxy.logger.isDebugEnabled()) {
    
    
            FilterChainProxy.logger.debug(UrlUtils.buildRequestUrl(this.firewalledRequest) + " at position " + this.currentPosition + " of " + this.size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'");
        }
		//迭代过滤器
        nextFilter.doFilter(request, response, this);
    }
}

在这里插入图片描述

3.处理认证请求

(1)判断是否是认证请求

AbstractAuthenticationProcessingFilter类——doFilter()

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    
    
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;
    //(1)判断是否是认证请求,如果不是,直接放行
    if (!this.requiresAuthentication(request, response)) {
    
    
        chain.doFilter(request, response);
    } else {
    
    
        //此时/login是认证的请求,执行else部分代码
        if (this.logger.isDebugEnabled()) {
    
    
            this.logger.debug("Request is to process authentication");
        }
		...
    }
}

(2)调用试图认证的方法

AbstractAuthenticationProcessingFilter类——doFilter()

如果是认证请求,调用试图认证的方法: attemptAuthentication()

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    
    
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;
    //(1)判断是否是认证请求,如果不是,直接放行
    if (!this.requiresAuthentication(request, response)) {
    
    
        chain.doFilter(request, response);
    } else {
    
    
        //此时/login是认证的请求,执行else部分代码
        if (this.logger.isDebugEnabled()) {
    
    
            this.logger.debug("Request is to process authentication");
        }
		//声明认证的token对象  authResult
        Authentication authResult;
        try {
    
    
            //(2)如果是认证请求,调用试图认证的方法: attemptAuthentication()
            authResult = this.attemptAuthentication(request, response);
            if (authResult == null) {
    
    
                return;
            }

            ...
    }
}

(3)判断请求类型是否为post

UsernamePasswordAuthenticationFilter类——attemptAuthentication()

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    
    
    //当前必须是post请求,如果不是则抛出异常
    if (this.postOnly && !request.getMethod().equals("POST")) {
    
    
        throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
    } else {
    
    
        //是post请求,则获取表求中的用户名与密码
        String username = this.obtainUsername(request);
        String password = this.obtainPassword(request);
        if (username == null) {
    
    
            username = "";
        }

        if (password == null) {
    
    
            password = "";
        }

        username = username.trim();
        //将用户名与密码封装至 UsernamePasswordAuthenticationToken对象中
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        this.setDetails(request, authRequest);
        //获取认证管理器ProviderManager对象,并调用认证的方法
        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

(4)做认证处理

通过ProviderManager认证管理器对象,调用authenticate()方法来做认证处理,在该方法中又去调用了一个认证器DaoAuthenticationProvider的authenticate()方法【注:该类没有此方法,需要从父类AbstractUserDetailsAuthenticationProvider继承中的方法authenticate()调用】

ProviderManager类——authenticate()

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    
    
    Class<? extends Authentication> toTest = authentication.getClass();
    AuthenticationException lastException = null;
    AuthenticationException parentException = null;
    Authentication result = null;
    Authentication parentResult = null;
    boolean debug = logger.isDebugEnabled();
    Iterator var8 = this.getProviders().iterator();

    while(var8.hasNext()) {
    
    
        AuthenticationProvider provider = (AuthenticationProvider)var8.next();
        if (provider.supports(toTest)) {
    
    
            if (debug) {
    
    
                logger.debug("Authentication attempt using " + provider.getClass().getName());
            }

            try {
    
    
                //进入这个方法
                result = provider.authenticate(authentication);
                if (result != null) {
    
    
                    this.copyDetails(authentication, result);
                    break;
                }
            ...
    }
}

AbstractUserDetailsAuthenticationProviderf类——authenticate()

调用retrieveUser()方法,查询用户. 封装成UserDetails接口对象返回

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    
    
    Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> {
    
    
        return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported");
    });
    String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName();
    boolean cacheWasUsed = true;
    UserDetails user = this.userCache.getUserFromCache(username);
    if (user == null) {
    
    
        cacheWasUsed = false;

        try {
    
    
            //查询用户. 以UserDetails接口对象返回
            user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
        ...
}
public interface UserDetails extends Serializable {
    
    
    //权限集合
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();  //密码

    String getUsername();  //用户名

    boolean isAccountNonExpired();  //true 表示没有过期 

    boolean isAccountNonLocked();  //是否没有被锁定

    boolean isCredentialsNonExpired(); //密码是否没有过期 

    boolean isEnabled();  //是否可用
}

(5)通过用户名查询用户对象

springSecurity框架自带的loadUserByUsername(username)方法

DaoAuthenticationProvider类——retrieveUser方法

protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    this.prepareTimingAttackProtection();

    try {
        //调用loadUserByUsername(username)方法
        UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
        } else {
            return loadedUser;
        }
    } 
    ...
}

InMemoryUserDetailsManager类——loadUserByUsername(username)方法

即从内存中读取username

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    UserDetails user = (UserDetails)this.users.get(username.toLowerCase());
    if (user == null) {
        throw new UsernameNotFoundException(username);
    } else {
        return new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(), user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
    }
}

这里的username我们可以在自定义的配置类WebSecurityConfig中配置

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //在内存中配置用户名和密码
        auth.inMemoryAuthentication().withUser("tom")
                .password(passwordEncoder.encode("123")).roles();
        auth.inMemoryAuthentication().withUser("admin")
                .password(passwordEncoder.encode("admin")).roles();
    }
}

②我们自定义的loadUserByUsername(username)方法

要想从自定义的数据库中读取账号和密码,同样可以在我们自定义的配置类WebSecurityConfig中配置,详见三、细节补充——(2)重写loadUserByUsername()方法

(6)检查用户是否通过认证

AbstractUserDetailsAuthenticationProvider类——authenticate()方法,调用preAuthenticationChecks.check()

   try {
    
    
       		//检验返回的用户是否可以使用
            this.preAuthenticationChecks.check(user);
            //将用户与提供的表单用户token进行比对
            this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
        } catch (AuthenticationException var7) {
    
    
           ...
   }
}         ....
               

DaoAuthenticationProvider类——additionalAuthenticationChecks()方法

验证(内存或数据库)用户名密码是否匹配成功

protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    
    
        if (authentication.getCredentials() == null) {
    
    
            this.logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
    
    
            String presentedPassword = authentication.getCredentials().toString();
            if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
    
    
                this.logger.debug("Authentication failed: password does not match stored value");
                throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }
    }

(7)通过认证,生成token

如果用户名与密码是正确的,则执行父类AbstractUserDetailsAuthenticationProvider中authenticate()中剩下的代码,最后返回return this.createSuccessAuthentication(principalToReturn, authentication, user)

DaoAuthenticationProvider类——createSuccessAuthentication()方法

 protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
    
    
        //根据给定的principal和密码信息,及权限列表重新生成一个token
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
        result.setDetails(authentication.getDetails());
        return result;
    }

而这个方法是重新为我们生成一个token,此token包装了用户的权限列表集。然后返回此token,接着一步步的向上返回这个 result结果对象

最终,返回到了源头AbstractAuthenticationProcessingFilter类的doFilter()方法,而此方法的末尾:

this.successfulAuthentication(request, response, chain, authResult);

此方法的关键:

protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
    
    
     	//将重新生成的token装载到SecurityContext中有很大作用---将来做鉴权时使用
        SecurityContextHolder.getContext().setAuthentication(authResult);
      

    	//此行代码就是认证成功后的处理器
        this.successHandler.onAuthenticationSuccess(request, response, authResult);
    }

this.successHandler.onAuthenticationSuccess(request, response, authResult);执行的是SavedRequestAwareAuthenticationSuccessHandler类中的onAuthenticationSucess()方法:

public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
    
    
        SavedRequest savedRequest = this.requestCache.getRequest(request, response);
        if (savedRequest == null) {
    
    
            super.onAuthenticationSuccess(request, response, authentication);
        } else {
    
    
            String targetUrlParameter = this.getTargetUrlParameter();
            if (!this.isAlwaysUseDefaultTargetUrl() && (targetUrlParameter == null || !StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
    
    
                this.clearAuthenticationAttributes(request);
                String targetUrl = savedRequest.getRedirectUrl();
                this.logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
                this.getRedirectStrategy().sendRedirect(request, response, targetUrl);
            } else {
    
    
                this.requestCache.removeRequest(request, response);
                super.onAuthenticationSuccess(request, response, authentication);
            }
        }
    }

由于认证成功,【只走到了部份的过滤器】,而我们没有去重写成功后的handler,所以根据security的配置,走默认的url -->/home再次请求,要走一个过滤器链11个

二、非认证请求(home)

1.spring管理过滤器的生命周期

在加载时启动一个叫DelegatingFilterProxy的过滤器代理对象,让spring管理这些过滤器的生命周期

DelegatingFilterProxy类——doFilter()

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
    
    //启动一个叫DelegatingFilterProxy的过滤器代理对象
    Filter delegateToUse = this.delegate;
    if (delegateToUse == null) {
    
    
        synchronized(this.delegateMonitor) {
    
    
            delegateToUse = this.delegate;
            if (delegateToUse == null) {
    
    
                WebApplicationContext wac = this.findWebApplicationContext();
                if (wac == null) {
    
    
                    throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
                }

                delegateToUse = this.initDelegate(wac);
            }

            this.delegate = delegateToUse;
        }
    }

    this.invokeDelegate(delegateToUse, request, response, filterChain);
}

2.初始化一个FilterChainProxy过滤器链代理对象

初始化一个过滤链FilterChainProxy过滤器链的代理对象,在这个过滤器链代理对象中有一个过滤器链集合,每一个过滤器链都有一组过滤器来处理不同的请求

FilterChainProxy类——doFilter()

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
    
    
    if (this.currentPosition == this.size) {
    
    
        if (FilterChainProxy.logger.isDebugEnabled()) {
    
    
            FilterChainProxy.logger.debug(UrlUtils.buildRequestUrl(this.firewalledRequest) + " reached end of additional filter chain; proceeding with original chain");
        }

        this.firewalledRequest.reset();
        this.originalChain.doFilter(request, response);
    } else {
    
    
        ++this.currentPosition;
        Filter nextFilter = (Filter)this.additionalFilters.get(this.currentPosition - 1);
        if (FilterChainProxy.logger.isDebugEnabled()) {
    
    
            FilterChainProxy.logger.debug(UrlUtils.buildRequestUrl(this.firewalledRequest) + " at position " + this.currentPosition + " of " + this.size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'");
        }
		//迭代过滤器
        nextFilter.doFilter(request, response, this);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tRe5Pi4C-1657945083180)(C:\Users\z\AppData\Roaming\Typora\typora-user-images\image-20220705201253348.png)]

3.处理认证请求

(1)判断是否是认证请求

AbstractAuthenticationProcessingFilter类——doFilter()

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    
    
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;
    //(1)判断是否是认证请求,如果不是,直接放行
    if (!this.requiresAuthentication(request, response)) {
    
    
        chain.doFilter(request, response);
    } else {
    
    
        //此时/login是认证的请求,执行else部分代码
        if (this.logger.isDebugEnabled()) {
    
    
            this.logger.debug("Request is to process authentication");
        }
		...
    }
}

此时不是认证请求,直接放行。

中间省略一些过滤器,走到第10个 ExceptionTranslationFilter异常转换过滤器,这个过滤器此时什么都不做,就是用来捕获后面过滤器抛出的异常。

ExceptionTranslationFilter类——doFilter()

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;

    try {
        //直接放行
        chain.doFilter(request, response);
        this.logger.debug("Chain processed normally");
    } catch 
        ...
    }

}

(2)从上下文中获取认证对象

然后又走到第11个过滤器:

FilterSecurityInterceptor类——doFilter()——invoke()

 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        this.invoke(fi);
    }


public void invoke(FilterInvocation fi) throws IOException, ServletException {
    
    
        if (fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
    
    
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } else {
    
    
            if (fi.getRequest() != null && this.observeOncePerRequest) {
    
    
                fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
            }
			
            //该过滤器内部,先调用beforeInvocation方法
            InterceptorStatusToken token = super.beforeInvocation(fi);

            try {
    
    
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            } finally {
    
    
                super.finallyInvocation(token);
            }

            super.afterInvocation(token, (Object)null);
        }

    }

FilterSecurityInterceptor类——beforeInvocation()方法

 protected InterceptorStatusToken beforeInvocation(Object object) {
    
     
          ....

             //判断当前SecurityContext上下文中是否存在Authentiation认证对象
              //此时不为空,因为自动添加了一个AnonymousUsername,但是并没有权限
             if (SecurityContextHolder.getContext().getAuthentication() == null) {
    
    
                    this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
                }

                Authentication authenticated = this.authenticateIfRequired();

                try {
    
    
                    this.accessDecisionManager.decide(authenticated, object, attributes);
                } catch (AccessDeniedException var7) {
    
    
                    this.publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, var7));
                    throw var7;
                }

                if (debug) {
    
    
                    this.logger.debug("Authorization successful");
                }

                if (this.publishAuthorizationSuccess) {
    
    
                    this.publishEvent(new AuthorizedEvent(object, attributes, authenticated));
                }

                Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
                if (runAs == null) {
    
    
                    if (debug) {
    
    
                        this.logger.debug("RunAsManager did not change Authentication object");
                    }

                    return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
                } else {
    
    
                    if (debug) {
    
    
                        this.logger.debug("Switching to RunAs Authentication: " + runAs);
                    }

                    SecurityContext origCtx = SecurityContextHolder.getContext();
                    SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
                    SecurityContextHolder.getContext().setAuthentication(runAs);
                    return new InterceptorStatusToken(origCtx, true, attributes, object);
                }
            } else if (this.rejectPublicInvocations) {
    
    
                throw new IllegalArgumentException("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'");
            } else {
    
    
                if (debug) {
    
    
                    this.logger.debug("Public object - authentication not attempted");
                }

                this.publishEvent(new PublicInvocationEvent(object));
                return null;
            }
        }
    }
在beforeInvocation()中,主要干了这几件事情:
1.调用SecurityContextHolder.getContext().getAuthentication(),获取当前 spring security的上下文对象。
2.从上下文对象中获取一个对象authentication(Token - 标志,象征)。
	2-1 当用户如果登录认证了,那么就把用户名,密码和用户权限封装为一个Authentication对象,Token).
	2-2 如果没有登录认证,则这个token对象不为空,则是一个匿名用户(anonymoususer),但是这个用户没有权限 ,所以无法调用到我们的目标方法,则会抛出一个异常throw new AccessDeniedException()表示没有权限,而拒绝访问。
此时用户未登录,抛出这个异常,会被第十一个过滤器ExceptionTranslationFilter捕获,然后调用handlerSpringSecurityException这个方法,接着调用sendStartAuthentication()这个方法:

(3)用户未登录,抛异常并重定向到登录页

sendStartAuthentication:

 protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
    
    
        SecurityContextHolder.getContext().setAuthentication((Authentication)null);
        this.requestCache.saveRequest(request, response);
        this.logger.debug("Calling Authentication entry point.");
        this.authenticationEntryPoint.commence(request, response, reason);
    }

在该方法中调用commence,此方法为一个接口对象的调用,执行的是LoginUrlAuthenticationEntryPoint实现类 中的commence()

public interface AuthenticationEntryPoint {
    
    
    void commence(HttpServletRequest var1, HttpServletResponse var2, AuthenticationException var3) throws IOException, ServletException;
}
public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {
    
    
     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
    
    
        String redirectUrl = null;
        if (this.useForward) {
    
    
            if (this.forceHttps && "http".equals(request.getScheme())) {
    
    
                redirectUrl = this.buildHttpsRedirectUrlForRequest(request);
            }

            if (redirectUrl == null) {
    
    
                String loginForm = this.determineUrlToUseForThisRequest(request, response, authException);
                if (logger.isDebugEnabled()) {
    
    
                    logger.debug("Server side forward to: " + loginForm);
                }

                RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);
                dispatcher.forward(request, response);
                return;
            }
        } else {
    
    
            redirectUrl = this.buildRedirectUrlToLoginPage(request, response, authException);
        }

        //发送重定向响应 http://localhost:8080/login.html
        this.redirectStrategy.sendRedirect(request, response, redirectUrl);
    }
}

在这个方法中主要是帮我们生成了一个 Url路径,然后重定向到这个路径,这样这一轮就走完了,

但是一个新的重定向请求 http://localhost:8080/login.html 发起,会再次向security发起请求,而这个请求又要被过滤器DelegatingFilterProxy拦截,然后又去初始化过滤器链,此时执行的是仍是这一组过滤器

当执行到最后一个过滤器FilterSecurityInterceptor时,调用beforeInvocation方法:

protected InterceptorStatusToken beforeInvocation(Object object) {
    
    
        Assert.notNull(object, "Object was null");
        boolean debug = this.logger.isDebugEnabled();
        if (!this.getSecureObjectClass().isAssignableFrom(object.getClass())) {
    
    
            throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName() + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + this.getSecureObjectClass());
        } else {
    
    
            //获取当前请求的url权限集合信息
            Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
            //当前/logint.html对应的atrributes为 premitAll
            if (attributes != null && !attributes.isEmpty()) {
    
    
                if (debug) {
    
    
                    this.logger.debug("Secure object: " + object + "; Attributes: " + attributes);
                }

                if (SecurityContextHolder.getContext().getAuthentication() == null) {
    
    
                    this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
                }

                //获取当前的authentication此时为anonymousUser匿名用户
                Authentication authenticated = this.authenticateIfRequired();

                 ...
                     
                     
                  return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);     
            }
        }
}

此时返回了一个拦截处理后的Token对象,然后放行,请求到达了/login.html目标资源,收到响应显示:

public void invoke(FilterInvocation fi) throws IOException, ServletException {
    if (fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
    } else {
        if (fi.getRequest() != null && this.observeOncePerRequest) {
            fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
        }

        InterceptorStatusToken token = super.beforeInvocation(fi);

        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.finallyInvocation(token);
        }

        super.afterInvocation(token, (Object)null);
    }

}

猜你喜欢

转载自blog.csdn.net/weixin_56039103/article/details/125818309