背景
專案中自定義了攔截器Filter
,專案中使用了spring security
,它也有對應的攔截器,我想讓我自定義的Filter
在spring security
的攔截器前執行。
因為我自定義的攔截器,需要提前做一些邏輯處理;然後spring security
的攔截器需要用到這部分的處理結果;所以我必須要想辦法讓我自定義的攔截器靠前執行。
那就一起來看看spring security
設定的攔截器的預設優先順序等級是多少吧。
模擬場景
自定義攔截器如下:
@Slf4j
public class MyOncePerRequestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
log.info("======== MyOncePerRequestFilter ========");
filterChain.doFilter(request, response);
}
}
@Configuration
public class Config {
@Bean
public FilterRegistrationBean<MyOncePerRequestFilter> i18nFilterRegistrationBean() {
FilterRegistrationBean<MyOncePerRequestFilter> registrationBean = new FilterRegistrationBean();
MyOncePerRequestFilter myOncePerRequestFilter = new MyOncePerRequestFilter();
registrationBean.setFilter(myOncePerRequestFilter);
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(-1);
return registrationBean;
}
}
spring security
的簡單配置如下:
@Slf4j
public class MyTokenStore implements TokenStore {
@Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
log.info("======== readAccessToken ========");
return new DefaultOAuth2AccessToken(tokenValue);
}
@Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
Authentication authentication = new AbstractAuthenticationToken(Sets.newHashSet()) {
{
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return StringUtils.EMPTY;
}
};
OAuth2Request request =
new OAuth2Request(null, null, null, true,
Sets.newHashSet(), Sets.newHashSet(), null, null, null);
return new OAuth2Authentication(request, authentication);
}
@Override public OAuth2Authentication readAuthentication(String token) {
return null;
}
@Override
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
}
@Override public void removeAccessToken(OAuth2AccessToken token) {
}
@Override public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
}
@Override public OAuth2RefreshToken readRefreshToken(String tokenValue) {
return null;
}
@Override public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return null;
}
@Override public void removeRefreshToken(OAuth2RefreshToken token) {
}
@Override public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
}
@Override public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
return null;
}
@Override public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
return null;
}
@Override public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
return null;
}
}
@Configuration
@EnableResourceServer
@EnableWebSecurity
public class MyResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
MyTokenStore tokenStore = new MyTokenStore();
resources.tokenStore(tokenStore);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and().anonymous().key("anonymousUser")
.and().httpBasic();
}
}
啟動類如下:
@RestController
public class MyController {
@GetMapping("/hello")
public String hello() {
return "hello,world!";
}
}
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Starter {
public static void main(String[] args) {
SpringApplication.run(Starter.class, args);
}
}
啟動後,訪問 http://127.0.0.1:8080/hello?access_token=123
日誌列印如下:
102149 [http-nio-8080-exec-1] INFO c.e.l.s.mvc.security.MyTokenStore - ======== readAccessToken ========
102149 [http-nio-8080-exec-1] INFO c.e.l.s.m.s.MyOncePerRequestFilter - ======== MyOncePerRequestFilter ========
從結果可以看出,spring security
的攔截器是比我們自定義的攔截器先執行的,而我們自定義的攔截器的優先順序是registrationBean.setOrder(-1)
我猜應該是這個值決定了執行順序,那就帶著這個猜想往下看一下吧。
是不是因為order
的值
在之前的配置中,我們將自定義的攔截器順序置為-1
我們先在MyOncePerRequestFilter.doFilterInternal
打個斷點,看一下執行鏈的順序:
從這條鏈中,我們猜測springSecurityFilterChain
的order
是-100
,我們自定義的攔截器是在它後面的
那我們直接把我們的攔截器設定成-101
,registrationBean.setOrder(-101);
,再來嘗試一下:
從斷點結果可以看出,我們的設定是有效的,並且起到了作用,而且列印日誌也說明了結果,如下:
11956 [http-nio-8080-exec-1] INFO c.e.l.s.m.s.MyOncePerRequestFilter - ======== MyOncePerRequestFilter ========
98419 [http-nio-8080-exec-1] INFO c.e.l.s.mvc.security.MyTokenStore - ======== readAccessToken ========
找出在哪裡賦予的order
值
這個過程是極其枯燥的,所以就先給結果了,如下:
spring security
的攔截器鏈是在下面這部分建立的:
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder()); // 這裡
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
}
public abstract class AbstractSecurityWebApplicationInitializer implements WebApplicationInitializer {
public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
}
@ConfigurationProperties(prefix = "spring.security")
public class SecurityProperties {
public static final int DEFAULT_FILTER_ORDER = OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100; // 這裡
private final Filter filter = new Filter();
public Filter getFilter() {
return this.filter;
}
public static class Filter {
private int order = DEFAULT_FILTER_ORDER; // 這裡
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
}
}
public interface OrderedFilter extends Filter, Ordered {
int REQUEST_WRAPPER_FILTER_MAX_ORDER = 0; // 這裡
}
從上面的程式碼可以看出,預設值是-100
,同樣也可以使用spring.security.filter.order
來自定義值。
下面是尋找此過程的歷程:
繼續從這裡開始,ApplicationFilterChain.internalDoFilter
如下:
可以看出所有的攔截器都是在filters
中,我們可以看這個值是怎麼來的,通過除錯,是在ApplicationFilterChain.addFilter
這個地方,如下:
它是被ApplicationFilterFactory.createFilterChain
呼叫的,如下:
所以filters
是根據filterMaps
來新增的,我們再來看一下filterMaps
是怎麼來的,一共涉及到兩個地方,如下:
StandardContext.addFilterMap
和StandardContext.addFilterMapBefore
如下:
看一下呼叫鏈:
原來是被ServletWebServerApplicationContext.selfInitialize
呼叫的,如下:
ServletWebServerApplicationContext.getServletContextInitializerBeans
如下:
ServletContextInitializerBeans
建構函式如下:
所有的攔截器都是通過addServletContextInitializerBeans(beanFactory);
和addAdaptableBeans(beanFactory);
來把bean
加進來的
經過一番除錯,終於找到spring security
這個攔截器定義順序的位置,SecurityFilterAutoConfiguration.securityFilterChainRegistration
如下:
可以看到SecurityProperties securityProperties
是注入進來的,找到這個類看一下,securityProperties.filter.order
如下:
@ConfigurationProperties(prefix = "spring.security")
public class SecurityProperties {
public static final int DEFAULT_FILTER_ORDER = OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100;
private final Filter filter = new Filter();
private final User user = new User();
public static class Filter {
/**
* Security filter chain order.
*/
private int order = DEFAULT_FILTER_ORDER;
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
}
}
public interface OrderedFilter extends Filter, Ordered {
/**
* Filters that wrap the servlet request should be ordered less than or equal to this.
*/
int REQUEST_WRAPPER_FILTER_MAX_ORDER = 0;
}
到此我們也找到了這個預設值,是根據spring.security.filter.order
來決定的,預設值是-100
解決辦法
第一種就是修改自己的順序:
@Configuration
public class Config {
@Bean
public FilterRegistrationBean<MyOncePerRequestFilter> i18nFilterRegistrationBean() {
FilterRegistrationBean<MyOncePerRequestFilter> registrationBean = new FilterRegistrationBean();
MyOncePerRequestFilter myOncePerRequestFilter = new MyOncePerRequestFilter();
registrationBean.setFilter(myOncePerRequestFilter);
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(-101); // 這裡
return registrationBean;
}
}
第二種就是修改spring security
攔截器的順序:
spring:
security:
filter:
order: 0
大家可以自己跑跑試試看,完結撒花~~~~~~