方法一、简单配置
@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 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 = 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 extends SecurityFilterChain> 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
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
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;
}
}