考虑这样一个场景,当咱们构建了一整套微服务架构的系统后,公司某个内部的老系统也感觉到了微服务架构的好处,包括实时监控,限流,熔断,高可用的机制等等,老系统的开发人员也但愿能减小本身的一些工做量,因此他们系统将老系统加入到咱们的微服务架构体系中来。这样就产生了一些适配,兼容性问题,若是让老系统来彻底适配已经构建好的微服务架构体系那么老系统改动的代价就比较大,包括技术的升级,开发人员的学习成本提升,测试问题,还有老系统还有一些不断的新需求要开发。比较理想的解决方案是对老系统的改动越小越好,最好能作到无缝集成,已经构建好的微服务架构来为老系统的集成提供支持。好比说老系统本来有本身的认证,受权控制,使用了 Spring Security ,在微服务架构中咱们将认证,受权的工做统一放在了 API 网关层去处理。这样就和老系统的集成产生了冲突。因而我就须要让 API 网关路由到老系统上的请求不通过老系统自身的认证、受权流程,也能够正常访问。同时也不能破坏当不经过 API 网关访问时老系统的认证、受权流程也要能正常工做。因此这是我要达到的目的。java
这是我在网上找的一张图,目的就是为了大概说明问题。 Spring Web Security 的核心功能都是在这一条过滤器链上完成的。具体能够参考这个类 :web
org.springframework.security.config.annotation.web.builders.FilterComparator , 这个类中定义了全部的 Spring Security 的过滤器以及他们的顺序。spring
搞清楚这一点,我就有一个想法,既然我想要关闭 Spring Security 不让他起做用 ,那我不让请求通过这些过滤器不就能够了么。架构
FilterComparator 源码 :app
private static final int STEP = 100; private Map<String, Integer> filterToOrder = new HashMap<String, Integer>(); FilterComparator() { int order = 100; put(ChannelProcessingFilter.class, order); order += STEP; put(ConcurrentSessionFilter.class, order); order += STEP; put(WebAsyncManagerIntegrationFilter.class, order); order += STEP; put(SecurityContextPersistenceFilter.class, order); order += STEP; put(HeaderWriterFilter.class, order); order += STEP; put(CorsFilter.class, order); order += STEP; put(CsrfFilter.class, order); order += STEP; put(LogoutFilter.class, order); order += STEP; put(X509AuthenticationFilter.class, order); order += STEP; put(AbstractPreAuthenticatedProcessingFilter.class, order); order += STEP; filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order); order += STEP; put(UsernamePasswordAuthenticationFilter.class, order); order += STEP; put(ConcurrentSessionFilter.class, order); order += STEP; filterToOrder.put( "org.springframework.security.openid.OpenIDAuthenticationFilter", order); order += STEP; put(DefaultLoginPageGeneratingFilter.class, order); order += STEP; put(ConcurrentSessionFilter.class, order); order += STEP; put(DigestAuthenticationFilter.class, order); order += STEP; put(BasicAuthenticationFilter.class, order); order += STEP; put(RequestCacheAwareFilter.class, order); order += STEP; put(SecurityContextHolderAwareRequestFilter.class, order); order += STEP; put(JaasApiIntegrationFilter.class, order); order += STEP; put(RememberMeAuthenticationFilter.class, order); order += STEP; put(AnonymousAuthenticationFilter.class, order); order += STEP; put(SessionManagementFilter.class, order); order += STEP; put(ExceptionTranslationFilter.class, order); order += STEP; put(FilterSecurityInterceptor.class, order); order += STEP; put(SwitchUserFilter.class, order); }
经过 debug 调试 能够发如今 Spring Security 提供的过滤器中使用的 FilterChain 的实际类型是这个类 : org.springframework.security.web.FilterChainProxy.VirtualFilterChain 。它实现了 FilterChain 接口。maven
经过搜索能够找到过滤器链条是在这个函数中进行初始化的 : org.springframework.security.config.annotation.web.builders.WebSecurity#performBuild 源码:ide
@Override protected Filter performBuild() throws Exception { Assert.state( !securityFilterChainBuilders.isEmpty(), "At least one SecurityBuilder<? extends SecurityFilterChain> 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 = ignoredRequests.size() + securityFilterChainBuilders.size(); List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>( chainSize); for (RequestMatcher ignoredRequest : ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); } FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); if (httpFirewall != null) { filterChainProxy.setFirewall(httpFirewall); } filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy; if (debugEnabled) { 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); } postBuildAction.run(); return result; }
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityFilterChain 源码:FilterChainProxy 自己也是一个过滤器这个过滤器会被注册到过滤器链上。而后这个过滤器内部封装了 Spring Security 的过滤器链条。函数
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); webSecurity.apply(adapter); } return webSecurity.build(); }
org.springframework.security.web.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); doFilterInternal(request, response, chain); } finally { SecurityContextHolder.clearContext(); request.removeAttribute(FILTER_APPLIED); } } else { doFilterInternal(request, response, chain); } }
org.springframework.security.web.FilterChainProxy#doFilterInternal 源码 : 这个函数是 Spring Security 过滤器链条的执行入口。每次请求都会 new 一个 VirtualFilterChain 的实例对象,而后调用该对象的 doFilter 函数,因而请求就进入到 Spring Security 的过滤器链处理中。post
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); }
org.springframework.security.web.FilterChainProxy.VirtualFilterChain#doFilter 源码 :这里就是去挨个调用 Spring Security 的过滤器的过程 ,重点须要关注的是 originalChain (原始的过滤器链条也就是 servlet 容器的) , currentPosition (spring security 过滤器链当前执行到的位置) , size (spring security 过滤器链中过滤器的个数) 。 当 currentPosition == size 的时候也就意味着 spring security 的过滤器链条执行完了,因而就该使用原始的 originalChain 继续去调用 servlet 容器中注册的过滤器了。
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (currentPosition == size) { if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " reached end of additional filter chain; proceeding with original chain"); } // Deactivate path stripping as we exit the security filter chain this.firewalledRequest.reset(); originalChain.doFilter(request, response); } else { currentPosition++; Filter nextFilter = additionalFilters.get(currentPosition - 1); if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " at position " + currentPosition + " of " + size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'"); } nextFilter.doFilter(request, response, this); } } }
了解上面讲的 spring security 过滤器链的执行过程后如何跳过spring security 的过滤器链就显而易见了 , 只须要控制 org.springframework.security.web.FilterChainProxy.Virt ualFilterChain 对象中的 currentPosition == size 就能够了。可是 org.springframework.security.web.FilterChainProxy.VirtualFilterChain 这个类是内部私有的静态成员类。 Spring Security 的目的就是为了封装它将它隐藏起来,想一想也能够理解毕竟这是它之因此能实现功能的核心,确定不但愿被乱动。可是没办法为了实现个人需求,我仍是要乱动它,想要改变它的话就只有经过反射的方式才能实现。
Class.forName("org.springframework.security.web.FilterChainProxy.VirtualFilterChain"); 使用这行代码的时候是不能成功获取到 VirtualFilterChain 类的 Class 对象的。由于 Java 中内部类在编译成 .class 文件后名称是这样的 FilterChainProxy$VirtualFilterChain 。想要成功获取到这个内部类的 Class 对象的话须要这样写 Class.forName("org.springframework.security.web.FilterChainProxy$VirtualFilterChain");
首先我自定义了一个过滤器而且把它加入到 Spring Security 过滤器链条中的最前面,由于个人目的是彻底的“关闭”掉spring security 的过滤器链。示例代码 :经过反射的方式改变 currentPosition 的值便可。
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (! isSkipOver(request)) { filterChain.doFilter(request , response); return; } Class<? extends FilterChain> filterChainClass = filterChain.getClass(); try { Class<?> virtualFilterChainClass = Class.forName("org.springframework.security.web.FilterChainProxy$VirtualFilterChain"); if (virtualFilterChainClass.isAssignableFrom(filterChainClass)) { Reflect reflect = Reflect.on(filterChain); Object size = reflect.field("size").get(); reflect.set("currentPosition" , size); } } catch (Throwable t) { throw new ApplicationRuntimeException(t); } filterChain.doFilter(request , response); }
若是你也面临到我所说的相似问题,须要编写一些代码来解决你的问题的话,那么上面这些代码已经不须要你再去花时间编写了,我已经写好了。你只须要经过 maven :
<dependency>
<groupId>org.hepeng</groupId>
<artifactId>hp-java-commons</artifactId>
<version>1.1.3</version>
</dependency>
或者是 gradle : implementation 'org.hepeng:hp-java-commons:1.1.3'
再自定义一个 Filter 而且继承 org.hepeng.commons.spring.security.web.filter.SkipOverSpringSecurityFilterChainFilter 便可。它已经通过了个人屡次测试能够正常工做,若是你发现任何 bug 能够向我反馈,我会尽快修复。