Spring Security 基本原理

  1. 三个重要的过滤器
    1. 权限过滤器 FilterSecurityInterceptor
    2. 异常处理过滤器 ExceptionTranslationFilter
    3. 用户密码认证过滤器 UsernamePasswordAuthenticationFilter

SpringSecurity 基本原理就是一个过滤器链:

org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
org.springframework.security.web.context.SecurityContextPersistenceFilter 
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter 
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter 
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter 
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter 
org.springframework.security.web.session.SessionManagementFilter 
org.springframework.security.web.access.ExceptionTranslationFilter 
org.springframework.security.web.access.intercept.FilterSecurityInterceptor

这些过滤器链哪里看呢,我们 debug 一下 DefaultSecurityFilterChain 就知道了

public final class DefaultSecurityFilterChain implements SecurityFilterChain {
    ...
    public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
        logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters));
        this.requestMatcher = requestMatcher;
        // 断点打这里
        this.filters = new ArrayList<>(filters);
    }
    ...
}

在 WebSecurity 中把 SecurityFilterChain 放入到 FilterChainProxy 中

public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
        implements SecurityBuilder<Filter>, ApplicationContextAware {
        ...
    @Override
    protected Filter performBuild() throws Exception {
        int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
        List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
        for (RequestMatcher ignoredRequest : this.ignoredRequests) {
            securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
        }
        for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
            securityFilterChains.add(securityFilterChainBuilder.build());
        }
        // 这里把 SecurityFilterChain 放入到 FilterChainProxy 中
        FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
        if (this.httpFirewall != null) {
            filterChainProxy.setFirewall(this.httpFirewall);
        }
        if (this.requestRejectedHandler != null) {
            filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
        }
        filterChainProxy.afterPropertiesSet();

        Filter result = filterChainProxy;
        if (this.debugEnabled) {
            result = new DebugFilter(filterChainProxy);
        }
        this.postBuildAction.run();
        return result;
    }
    ...
}

这其实是一种责任链的设计模式。在这些过滤器都在同一条过滤器链上,即通过chain.doFilter(request, response)可将请求一层接一层转发

public class FilterChainProxy extends GenericFilterBean {

    private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
        FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
        HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
        // 得到所有的过滤器,加载的过滤链中
        List<Filter> filters = getFilters(firewallRequest);
        if (filters == null || filters.size() == 0) {
            if (logger.isTraceEnabled()) {
                logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
            }
            firewallRequest.reset();
            chain.doFilter(firewallRequest, firewallResponse);
            return;
        }
        if (logger.isDebugEnabled()) {
            logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
        }
        VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);
        virtualFilterChain.doFilter(firewallRequest, firewallResponse);
    }

}

三个重要的过滤器

FilterSecurityInterceptor:是一个方法级的权限过滤器,基本位于过滤器链的最底部
ExceptionTranslationFilter:是一个异常过滤器,处理在认证授权过程中抛出的异常
UsernamePasswordAuthenticationFilter:对登录接口的POST请求做拦截,校验表单中用户名、密码

权限过滤器 FilterSecurityInterceptor

1、调用父类的 beforeInvocation 方法,传入 new FilterInvocation(request, response, chain),然后返回结果为 InterceptorStatusToken,真正的前置权限认证就在其中判断

2、过滤器链继续往下执行,实际上就是在执行业务代码

3、调用父类的 finallyInvocation 方法,传入第一步返回的 InterceptorStatusToken,主要是根据 RunAsManager 是否返回结果来还原之前的认证信息

4、调用父类的 afterInvocation 方法,传入第一步返回的 InterceptorStatusToken,主要是对最终返回的结果进一步处理

public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
    if (isApplied(filterInvocation) && this.observeOncePerRequest) {
        filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
        return;
    }
    if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
        filterInvocation.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
    }
    // 1、真正的前置权限认证就在其中判断
    InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
    try {
        // 2、执行业务代码
        filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
    }
    finally {
        // 3、根据 RunAsManager 是否返回结果来还原之前的认证信息
        super.finallyInvocation(token);
    }
    // 4、对最终返回的结果进一步处理
    super.afterInvocation(token, null);
}

异常处理过滤器 ExceptionTranslationFilter

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    try {
        // 执行过滤链,一旦发生异常进入 catch
        chain.doFilter(request, response);
    }
    catch (IOException ex) {
        throw ex;
    }
    catch (Exception ex) {
        // 从堆栈中提取 SpringSecurity 异常
        Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
        RuntimeException securityException = (AuthenticationException) this.throwableAnalyzer
            .getFirstThrowableOfType(AuthenticationException.class, causeChain);
        if (securityException == null) {
            securityException = (AccessDeniedException) this.throwableAnalyzer
                .getFirstThrowableOfType(AccessDeniedException.class, causeChain);
        }
        if (securityException == null) {
            rethrow(ex);
        }
        if (response.isCommitted()) {
            throw new ServletException("Unable to handle the Spring Security Exception "
                                       + "because the response is already committed.", ex);
        }
        handleSpringSecurityException(request, response, chain, securityException);
    }
}

用户密码认证过滤器 UsernamePasswordAuthenticationFilter

@Override
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());
    }
    String username = obtainUsername(request);
    username = (username != null) ? username : "";
    username = username.trim();
    String password = obtainPassword(request);
    password = (password != null) ? password : "";
    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
    // Allow subclasses to set the "details" property
    setDetails(request, authRequest);
    return this.getAuthenticationManager().authenticate(authRequest);
}

转载请注明来源。 欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。 可以在下面评论区评论,也可以邮件至 sharlot2050@foxmail.com。

文章标题:Spring Security 基本原理

字数:1k

本文作者:夏来风

发布时间:2021-06-04, 23:00:00

原始链接:http://www.demo1024.com/blog/spring-security-theory/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。