前言
跨域:两个域名的(协议、域名/ip、端口)有任意一个不同即视为跨域 跨域资源共享(Cors):即浏览器允许访问其他跨域的资源 而CorsFilter就是SpringSecurity用来处理Cors的过滤器
1. CorsConfigurer
CorsConfigurer是CorsFilter对应的配置类,其中就只有一个重要方法
1.1 configure(…)
configure(…)源码很简单,主要是调用了getCorsFilter(…)方法
@Override
public void configure ( H http) {
ApplicationContext context = http. getSharedObject ( ApplicationContext . class ) ;
CorsFilter corsFilter = getCorsFilter ( context) ;
Assert . state ( corsFilter != null , ( ) -> "Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a "
+ CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean." ) ;
http. addFilter ( corsFilter) ;
}
getCorsFilter(…):
容器中若是有CorsFilter直接返回 容器中若是有CorsConfigurationSource,那就为其包装为CorsFilter返回
private CorsFilter getCorsFilter ( ApplicationContext context) {
if ( this . configurationSource != null ) {
return new CorsFilter ( this . configurationSource) ;
}
boolean containsCorsFilter = context. containsBeanDefinition ( CORS_FILTER_BEAN_NAME ) ;
if ( containsCorsFilter) {
return context. getBean ( CORS_FILTER_BEAN_NAME , CorsFilter . class ) ;
}
boolean containsCorsSource = context. containsBean ( CORS_CONFIGURATION_SOURCE_BEAN_NAME ) ;
if ( containsCorsSource) {
CorsConfigurationSource configurationSource = context. getBean ( CORS_CONFIGURATION_SOURCE_BEAN_NAME ,
CorsConfigurationSource . class ) ;
return new CorsFilter ( configurationSource) ;
}
boolean mvcPresent = ClassUtils . isPresent ( HANDLER_MAPPING_INTROSPECTOR , context. getClassLoader ( ) ) ;
if ( mvcPresent) {
return MvcCorsFilter . getMvcCorsFilter ( context) ;
}
return null ;
}
1.2 CorsConfiguration
CorsConfigurationSource是根据请求获取CorsConfiguration,也就Cors规则 简单的看下里面的规则
private static final List < String > DEFAULT_PERMIT_METHODS = Collections . unmodifiableList (
Arrays . asList ( HttpMethod . GET . name ( ) , HttpMethod . HEAD . name ( ) , HttpMethod . POST . name ( ) ) ) ;
private static final List < String > DEFAULT_PERMIT_ALL = Collections . singletonList ( ALL ) ;
@Nullable
private List < String > allowedOrigins;
@Nullable
private List < String > allowedMethods;
@Nullable
private List < HttpMethod > resolvedMethods = DEFAULT_METHODS ;
@Nullable
private List < String > allowedHeaders;
@Nullable
private List < String > exposedHeaders;
@Nullable
private Boolean allowCredentials;
@Nullable
private Long maxAge;
2. CorsFilter
作为SpringSecurity处理Cors的过滤器,其源码很少 就是从CorsConfigurationSource中获取CorsConfiguration,然后把CorsProcessor丢进CorsProcessor判断是否跨域请求以及对跨域请求的处理
public class CorsFilter extends OncePerRequestFilter {
private final CorsConfigurationSource configSource;
private CorsProcessor processor = new DefaultCorsProcessor ( ) ;
. . .
@Override
protected void doFilterInternal ( HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException , IOException {
CorsConfiguration corsConfiguration = this . configSource. getCorsConfiguration ( request) ;
boolean isValid = this . processor. processRequest ( corsConfiguration, request, response) ;
if ( ! isValid || CorsUtils . isPreFlightRequest ( request) ) {
return ;
}
filterChain. doFilter ( request, response) ;
}
. . .
}
2.1 DefaultCorsProcessor
CorsProcessor是Spring-Web包下的,其实现也只有一个DefaultCorsProcessor 我们看下其入口方法,主要就是非跨域请求直接放行,不是的话就调用handleInternal(…)方法
@Override
@SuppressWarnings ( "resource" )
public boolean processRequest ( @Nullable CorsConfiguration config, HttpServletRequest request,
HttpServletResponse response) throws IOException {
Collection < String > varyHeaders = response. getHeaders ( HttpHeaders . VARY ) ;
if ( ! varyHeaders. contains ( HttpHeaders . ORIGIN ) ) {
response. addHeader ( HttpHeaders . VARY , HttpHeaders . ORIGIN ) ;
}
if ( ! varyHeaders. contains ( HttpHeaders . ACCESS_CONTROL_REQUEST_METHOD ) ) {
response. addHeader ( HttpHeaders . VARY , HttpHeaders . ACCESS_CONTROL_REQUEST_METHOD ) ;
}
if ( ! varyHeaders. contains ( HttpHeaders . ACCESS_CONTROL_REQUEST_HEADERS ) ) {
response. addHeader ( HttpHeaders . VARY , HttpHeaders . ACCESS_CONTROL_REQUEST_HEADERS ) ;
}
if ( ! CorsUtils . isCorsRequest ( request) ) {
return true ;
}
if ( response. getHeader ( HttpHeaders . ACCESS_CONTROL_ALLOW_ORIGIN ) != null ) {
logger. trace ( "Skip: response already contains " Access - Control - Allow - Origin "" ) ;
return true ;
}
boolean preFlightRequest = CorsUtils . isPreFlightRequest ( request) ;
if ( config == null ) {
if ( preFlightRequest) {
rejectRequest ( new ServletServerHttpResponse ( response) ) ;
return false ;
}
else {
return true ;
}
}
return handleInternal ( new ServletServerHttpRequest ( request) , new ServletServerHttpResponse ( response) , config, preFlightRequest) ;
}
我们再看下handleInternal(…)方法:如果是预检查(跨域)请求检查是否支持,其他类型的直接返回True
本质上就是在满足跨域规则的情况下,添加对应的响应头
protected boolean handleInternal ( ServerHttpRequest request, ServerHttpResponse response,
CorsConfiguration config, boolean preFlightRequest) throws IOException {
String requestOrigin = request. getHeaders ( ) . getOrigin ( ) ;
String allowOrigin = checkOrigin ( config, requestOrigin) ;
if ( allowOrigin == null ) {
logger. debug ( "Reject: '" + requestOrigin + "' origin is not allowed" ) ;
rejectRequest ( response) ;
return false ;
}
HttpHeaders responseHeaders = response. getHeaders ( ) ;
HttpMethod requestMethod = getMethodToUse ( request, preFlightRequest) ;
List < HttpMethod > allowMethods = checkMethods ( config, requestMethod) ;
if ( allowMethods == null ) {
logger. debug ( "Reject: HTTP '" + requestMethod + "' is not allowed" ) ;
rejectRequest ( response) ;
return false ;
}
List < String > requestHeaders = getHeadersToUse ( request, preFlightRequest) ;
List < String > allowHeaders = checkHeaders ( config, requestHeaders) ;
if ( preFlightRequest && allowHeaders == null ) {
logger. debug ( "Reject: headers '" + requestHeaders + "' are not allowed" ) ;
rejectRequest ( response) ;
return false ;
}
responseHeaders. setAccessControlAllowOrigin ( allowOrigin) ;
if ( preFlightRequest) {
responseHeaders. setAccessControlAllowMethods ( allowMethods) ;
}
if ( preFlightRequest && ! allowHeaders. isEmpty ( ) ) {
responseHeaders. setAccessControlAllowHeaders ( allowHeaders) ;
}
if ( ! CollectionUtils . isEmpty ( config. getExposedHeaders ( ) ) ) {
responseHeaders. setAccessControlExposeHeaders ( config. getExposedHeaders ( ) ) ;
}
if ( Boolean . TRUE . equals ( config. getAllowCredentials ( ) ) ) {
responseHeaders. setAccessControlAllowCredentials ( true ) ;
}
if ( preFlightRequest && config. getMaxAge ( ) != null ) {
responseHeaders. setAccessControlMaxAge ( config. getMaxAge ( ) ) ;
}
response. flush ( ) ;
return true ;
}