Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

运行时动态的开关 Spring Security 原 荐

0
0
1. 为什么要在运行时动态的开关 Spring Security?

考虑这样一个场景,当我们构建了一整套微服务架构的系统后,公司某个内部的老系统也感受到了微服务架构的好处,包括实时监控,限流,熔断,高可用的机制等等,老系统的开发人员也希望能减少自己的一些工作量,所以他们系统将老系统加入到我们的微服务架构体系中来。这样就产生了一些适配,兼容性问题,如果让老系统来完全适配已经构建好的微服务架构体系那么老系统改动的代价就比较大,包括技术的升级,开发人员的学习成本提高,测试问题,还有老系统还有一些不断的新需求要开发。比较理想的解决方案是对老系统的改动越小越好,最好能做到无缝集成,已经构建好的微服务架构来为老系统的集成提供支持。比如说老系统原本有自己的认证,授权控制,使用了 Spring Security ,在微服务架构中我们将认证,授权的工作统一放在了 API 网关层去处理。这样就和老系统的集成产生了冲突。于是我就需要让 API 网关路由到老系统上的请求不经过老系统自身的认证、授权流程,也可以正常访问。同时也不能破坏当不通过 API 网关访问时老系统的认证、授权流程也要能正常工作。所以这是我要达到的目的。

2. Spring Security 在 Web 项目中是如何工作的

这是我在网上找的一张图,目的就是为了大概说明问题。 Spring WebSecurity 的核心功能都是在这一条过滤器链上完成的。具体可以参考这个类 :

org.springframework.security.config.annotation.web.builders.FilterComparator , 这个类中定义了所有的 Spring Security 的过滤器以及他们的顺序。

搞清楚这一点,我就有一个想法,既然我想要关闭 Spring Security 不让他起作用 ,那我不让请求经过这些过滤器不就可以了么。


运行时动态的开关 Spring Security 原 荐

FilterComparator 源码 :

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);
} 3. Spring Security 的过滤器链是如何工作的 3.1 Spring Security 过滤器链是什么 ?

通过 debug 调试 可以发现在 Spring Security 提供的过滤器中使用的 FilterChain 的实际类型是这个类 :org.springframework.security.web.FilterChainProxy.VirtualFilterChain 。它实现了 FilterChain 接口。

3.2 Spring Security 过滤器链初始化

通过搜索可以找到过滤器链条是在这个函数中进行初始化的 :org.springframework.security.config.annotation.web.builders.WebSecurity#performBuild 源码:

@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();
} 3.3 Spring Security 过滤器链的工作过程

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 的过滤器链处理中。

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 继续去调用 ser

Viewing all articles
Browse latest Browse all 12749