Spring Boot 自动装配 Spring Security 详解

Spring Boot 自动装配 Spring Security 详解

Spring Security 是 Java Web 技术中,管理认证授权安全框架

Spring Security 原理(Servlet)

在 Spring MVC 项目中,Spring Security 在 Web 容器中插入一个 Filter,从而在 Http 请求进入 DispatcherServlet 前对认证(Authentication)和授权(Authorization)进行校验。

关于 Spring Security 的更多架构原理请参看 Servlet 架构

关键结构

                     springSecurityFilterrChain
                                  :
                                  :
DelegatingFilterProxy --> FilterChainProxy --> SecurityFilterChain
                                  ^                     ^
                                  |                     |
                             WebSecurity           HttpSecurity

Spring Security 框架应用了职责链模式,通过多级 Filter 的对请求的层层拦截实现了对不同职责关注点的解耦。

1. SecurityFilterChain 和 FilterChainProxy

Spring Security 框架中表示职责链的接口是 SecurityFilterChain,具有 matches() 和 getFilters() 两个方法。因为 Spring Security 可以配置多个 SecurityFilterChain,所以需要 matches() 方法根据具体的请求完成选择。getFilters() 方法则是用来获取职责链中的 Filter 的有序列表。

package org.springframework.security.web;
// ...
public interface SecurityFilterChain {
   

    boolean matches(HttpServletRequest request);

    List<Filter> getFilters();
}

FilterChainProxy 是 SecurityFilterChain 集合的代理,它将多个 SecurityFilterChain 包装起来,对外暴露统一的入口。

package org.springframework.security.web;
// ...
public class FilterChainProxy extends GenericFilterBean {
   
    // ...
    private List<SecurityFilterChain> filterChains;
    // ...
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
   
        boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
        if (clearContext) {
   
            try {
   
                request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
                doFilterInternal(request, response, chain);
            }
            finally {
   
                SecurityContextHolder.clearContext();
                request.removeAttribute(FILTER_APPLIED);
            }
        }
        else {
   
            doFilterInternal(request, response, chain);
        }
    }

    private void doFilterInternal(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
   

        FirewalledRequest fwRequest = firewall
                .getFirewalledRequest((HttpServletRequest) request);
        HttpServletResponse fwResponse = firewall
                .getFirewalledResponse((HttpServletResponse) response);

        List<Filter> filters = getFilters(fwRequest);

        if (filters == null || filters.size() == 0) {
   
            if (logger.isDebugEnabled()) {
   
                logger.debug(UrlUtils.buildRequestUrl(fwRequest)
                        + (filters == null ? " has no matching filters"
                                : " has an empty filter list"));
            }

            fwRequest.reset();

            chain.doFilter(fwRequest, fwResponse);

            return;
        }

        VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
        vfc.doFilter(fwRequest, fwResponse);
    }

    private List<Filter> getFilters(HttpServletRequest request) {
   
        for (SecurityFilterChain chain : filterChains) {
   
            if (chain.matches(request)) {
   
                return chain.getFilters();
            }
        }

        return null;
    }
    // ...
}

2. DelegatingFilterProxy

DelegatingFilterProxy 是 FilterChainProxy 的代理,是 Spring Security 框架与 Web 容器关联的 Filter 对象,其 initDelegate() 方法根据 targetBeanName 来获取真正作为 Filter 的 Bean。

事实上,代理 FilterChainProxy 对象的 DelegatingFilterProxy 对象的 targetBeanName 成员的值是 springSecurityFilterChain,即 DelegatingFilterProxy 对象代理了名为 springSecurityFilterChain 的 Bean。

package org.springframework.web.filter;
// ...
public class DelegatingFilterProxy extends GenericFilterBean {
   
    // ...
    @Nullable
    private volatile Filter delegate;
    // ...
    public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {
   
        Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
        this.setTargetBeanName(targetBeanName);
        this.webApplicationContext = wac;
        if (wac != null) {
   
            this.setEnvironment(wac.getEnvironment());
        }
    }
    // ...
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
   

        // Lazily initialize the delegate if necessary.
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
   
            synchronized (this.delegateMonitor) {
   
                delegateToUse = this.delegate;
                if (delegateToUse == null) {
   
                    WebApplicationContext wac = findWebApplicationContext();
                    if (wac == null) {
   
                        throw new IllegalStateException("No WebApplicationContext found: " +
                                "no ContextLoaderListener or DispatcherServlet registered?");
                    }
                    delegateToUse = initDelegate(wac);
                }
                this.delegate = delegateToUse;
            }
        }

        // Let the delegate perform the actual doFilter operation.
        invokeDelegate(delegateToUse, request, response, filterChain);
    }
    // ...
    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
   
        String targetBeanName = getTargetBeanName();
        Assert.state(targetBeanName != null, "No target bean name set");
        Filter delegate = wac.getBean(targetBeanName, Filter.class);
        if (isTargetFilterLifecycle()) {
   
            delegate.init(getFilterConfig());
        }
        return delegate;
    }
    // ...
}

3. WebSecurity 和 HttpSecurity

最后,WebSecurityFilterChainProxy 的 Builder 类,HttpSecurityDefaultSecurityFilterChain 的 Builder 类。它们都继承了 AbstractConfiguredSecurityBuilder 类,而 AbstractConfiguredSecurityBuilder 又继承 AbstractSecurityBuilder 类,AbstractSecurityBuilder 则实现了 SecurityBuilder 接口。

3.1. SecurityBuilder 接口

SecurityBuilder 接口中只定义了 build() 方法。

package org.springframework.security.config.annotation;
// ...
public interface SecurityBuilder<O> {
   

    O build() throws Exception;
}
3.2. AbstractSecurityBuilder 抽象类

AbstractSecurityBuilder 的 build() 方法直接调用 doBuild() 方法。

package org.springframework.security.config.annotation;
// ...
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
   
    // ...
    public final O build() throws Exception {
   
        if (this.building.compareAndSet(false, true)) {
   
            this.object = doBuild();
            return this.object;
        }
        throw new AlreadyBuiltException("This object has already been built");
    }
    // ...
    protected abstract O doBuild() throws Exception;
    // ...
}
3.4. AbstractConfiguredSecurityBuilder 抽象类

AbstractConfiguredSecurityBuilder 重写了 doBuild() 方法,依次调用 beforeInit(), init(), beforeConfigure(), configure() 方法进行初始化和配置,最后调用 performBuild() 方法完成对象创建过程。

package org.springframework.security.config.annotation;
// ...
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
        extends AbstractSecurityBuilder
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值