spring security 动态不拦截指定请求

方法一、简单配置

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        //ignore
        web.ignoring().antMatchers("/info","/css","/html");
    }
}

缺点:每次要新增一个免登陆的url的时候,就得重新启动一遍,不灵活。

方法二、动态增加不拦截请求

通过查看源码org.springframework.security.config.annotation.web.builders.WebSecurity

protected Filter performBuild() throws Exception {
        Assert.state(!this.securityFilterChainBuilders.isEmpty(), "At least one SecurityBuilder needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke " + WebSecurity.class.getSimpleName() + ".addSecurityFilterChainBuilder directly");
        int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
        List securityFilterChains = new ArrayList(chainSize);
        Iterator var3 = this.ignoredRequests.iterator();

        while(var3.hasNext()) {
            RequestMatcher ignoredRequest = (RequestMatcher)var3.next();
            securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest, new Filter[0]));
        }

        var3 = this.securityFilterChainBuilders.iterator();

        while(var3.hasNext()) {
            SecurityBuilder securityFilterChainBuilder = (SecurityBuilder)var3.next();
            securityFilterChains.add(securityFilterChainBuilder.build());
        }

        FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
        if (this.httpFirewall != null) {
            filterChainProxy.setFirewall(this.httpFirewall);
        }

        filterChainProxy.afterPropertiesSet();
        Filter result = filterChainProxy;
        if (this.debugEnabled) {
            this.logger.warn("\n\n********************************************************************\n**********        Security debugging is enabled.       *************\n**********    This may include sensitive information.  *************\n**********      Do not use in a production system!     *************\n********************************************************************\n\n");
            result = new DebugFilter(filterChainProxy);
        }

        this.postBuildAction.run();
        return (Filter)result;
    }

DefaultSecurityFilterChain的构造器如下

public final class DefaultSecurityFilterChain implements SecurityFilterChain {
    private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
    private final RequestMatcher requestMatcher;
    private final List filters;

    public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
        this(requestMatcher, Arrays.asList(filters));
    }

    public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List filters) {
        logger.info("Creating filter chain: " + requestMatcher + ", " + filters);
        this.requestMatcher = requestMatcher;
        this.filters = new ArrayList(filters);
    }

    public RequestMatcher getRequestMatcher() {
        return this.requestMatcher;
    }

    public List getFilters() {
        return this.filters;
    }

    public boolean matches(HttpServletRequest request) {
        return this.requestMatcher.matches(request);
    }

    public String toString() {
        return "[ " + this.requestMatcher + ", " + this.filters + "]";
    }
}

 从上面源码可以看到ignore请求会被封装成DefaultSecurityFilterChain,然后添加到securityFilterChains,然后将securityFilterChains交给FilterChainProxy代理,接下来看下FilterChainProxy的doFilter方法

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);
                this.doFilterInternal(request, response, chain);
            } finally {
                SecurityContextHolder.clearContext();
                request.removeAttribute(FILTER_APPLIED);
            }
        } else {
            this.doFilterInternal(request, response, chain);
        }

    }

    private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FirewalledRequest fwRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
        HttpServletResponse fwResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
        List filters = this.getFilters((HttpServletRequest)fwRequest);
        if (filters != null && filters.size() != 0) {
            FilterChainProxy.VirtualFilterChain vfc = new FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
            vfc.doFilter(fwRequest, fwResponse);
        } else {
            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);
        }
    }

    private List getFilters(HttpServletRequest request) {
        Iterator var2 = this.filterChains.iterator();

        SecurityFilterChain chain;
        do {
            if (!var2.hasNext()) {
                return null;
            }

            chain = (SecurityFilterChain)var2.next();
        } while(!chain.matches(request));

        return chain.getFilters();
    }

从上面源码可以出,只需要从 FilterChainProxy中获取到属性List filterChains值,然后动态往里面添加ignore请求即可。从源码HttpSecurityBeanDefinitionParser类的对象,可以在这个类中看到段静态代码,内容如下:

static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
        if (!pc.getRegistry().containsBeanDefinition("org.springframework.security.filterChainProxy")) {
            BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
            listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
            pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, "org.springframework.security.filterChains"));
            BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
            fcpBldr.getRawBeanDefinition().setSource(source);
            fcpBldr.addConstructorArgReference("org.springframework.security.filterChains");
            fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
            BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
            pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, "org.springframework.security.filterChainProxy"));
            pc.getRegistry().registerAlias("org.springframework.security.filterChainProxy", "springSecurityFilterChain");
        }
    }

可以看到pc.getRegistry().registerAlias("org.springframework.security.filterChainProxy", "springSecurityFilterChain");这里我们可以知道FilterChainProxy的bean的名字叫做springSecurityFilterChain。

因此我们的具体实现如下:

1.拿到FilterChainProxy 

2.通过反射拿到List securityFilterChains

3.动态往securityFilterChains里面添加ignore请求urls即可

public void ignoreUrlAuthenication(String... urls) {
        FilterChainProxy obj = (FilterChainProxy) SpringContextUtil.getApplicationContext().getBean("springSecurityFilterChain");
        List securityFilterChains = (List) getProperty(obj,"filterChains");
        for (String url : urls) {
            securityFilterChains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher(url), new Filter[0]));
        }
       
    }

    private static Object getProperty(Object obj, String fieldName) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

 

你可能感兴趣的:(spring,security)