SpringBoot跨域处理
@CrossOrigin(局部跨域)
作用在方法上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @RestController public class IndexController { @CrossOrigin(value = "http://localhost:8082") @GetMapping("/hello") public String hello () { return "get hello" ; } @CrossOrigin(value = "http://localhost:8082") @PostMapping("/hello") public String hello2 () { return "post hello" ; } }
作用在类上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @CrossOrigin(origins = "http://example.com", maxAge = 3600) @RestController @RequestMapping("/account") public class AccountController { @RequestMapping(method = RequestMethod.GET, path = "/{id}") public Account retrieve (@PathVariable Long id) { } @RequestMapping(method = RequestMethod.DELETE, path = "/{id}") public void remove (@PathVariable Long id) { } }
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings (CorsRegistry registry) { registry.addMapping("/**" ) .allowedHeaders("Content-Type" ,"X-Requested-With" ,"accept,Origin" ,"Access-Control-Request-Method" ,"Access-Control-Request-Headers" ,"token" ) .allowedMethods("*" ) .allowedOrigins("*" ) .allowCredentials(true ); } }
SpringSecurity跨域处理
跨域配置详解
如果使用了 Spring Security,上面的跨域配置会失效,因为请求被 Spring Security 拦截了
在项目中使用 Spring Security,我们必须采取额外的步骤确保它与 CORS 协作良好。这是因为 CORS 需要首先处理,否则,Spring Security 会在请求到达 Spring MVC 之前将其拒绝
1 2 3 4 5 6 7 8 @Configuration public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (HttpSecurity http) throws Exception { http.cors().and()... } }
可以配置 CORS 以覆盖默认的 Spring Security CORS 处理器。为此,我们需要添加一个 CorsConfigurationSource Bean,使用 CorsConfiguration 实例来处理 CORS 配置。如果添加了 CorsFilter Bean,http.cors() 方法就会使用 CorsFilter,否则就会使用 CorsConfigurationSource。如果两者都未配置,则使用 Spring MVC pattern inspector handler。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public class CorsConfigurer <H extends HttpSecurityBuilder <H>> extends AbstractHttpConfigurer <CorsConfigurer<H>, H> { private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector" ; private static final String CORS_CONFIGURATION_SOURCE_BEAN_NAME = "corsConfigurationSource" ; private static final String CORS_FILTER_BEAN_NAME = "corsFilter" ; private CorsConfigurationSource configurationSource; public CorsConfigurer () { } public CorsConfigurer<H> configurationSource ( CorsConfigurationSource configurationSource) { this .configurationSource = configurationSource; return this ; } @Override public void configure (H http) { ApplicationContext context = http.getSharedObject(ApplicationContext.class); CorsFilter corsFilter = getCorsFilter(context); if (corsFilter == null ) { throw new IllegalStateException ( "Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a " + CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean." ); } http.addFilter(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 ; } static class MvcCorsFilter { private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector" ; private static CorsFilter getMvcCorsFilter (ApplicationContext context) { if (!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) { throw new NoSuchBeanDefinitionException (HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName() + " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext." ); } HandlerMappingIntrospector mappingIntrospector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class); return new CorsFilter (mappingIntrospector); } } }
CorsConfigurationSource(全局跨域)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Configuration public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() .permitAll() .and() .formLogin() .permitAll() .and() .httpBasic() .and() .cors() .and() .csrf() .disable(); } @Bean public CorsConfigurationSource corsConfigurationSource () { CorsConfiguration configuration = new CorsConfiguration (); configuration.setAllowedOrigins(Arrays.asList("*" )); configuration.setAllowedMethods(Arrays.asList("*" )); configuration.setAllowedHeaders(Arrays.asList("*" )); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource (); source.registerCorsConfiguration("/**" , configuration); return source; } }
CosFilter(全局跨域)
还有一种情况就是支持 OAuth2 相关接口的跨域,比如用户要访问 OAuth2 中的 /oauth/token 等接口,可以配置一个全局的 CorsFilter 跨域过滤器类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Configuration public class GlobalCorsConfig { @Bean public CorsFilter corsFilter () { CorsConfiguration config = new CorsConfiguration (); config.addAllowedOrigin("*" ); config.setAllowCredentials(true ); config.addAllowedMethod("*" ); config.addAllowedHeader("*" ); config.addExposedHeader("*" ); UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource (); configSource.registerCorsConfiguration("/**" , config); return new CorsFilter (configSource); } }
自定义Filter(全局跨域)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class CorsFilter extends OncePerRequestFilter { @Override protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String orignalHeader = StringUtils.defaultIfBlank(request.getHeader("Origin" ), "*" ); response.setHeader("Access-Control-Max-Age" , "3600" ); response.setHeader("Access-Control-Allow-Headers" , request.getHeader("Access-Control-Request-Headers" )); response.setHeader("Access-Control-Allow-Origin" , orignalHeader); response.setHeader("Access-Control-Allow-Credentials" , "true" ); response.setHeader("Access-Control-Allow-Methods" , "POST, GET, OPTIONS, DELETE, PUT" ); filterChain.doFilter(request, response); } }
1 2 3 4 5 6 7 8 @Configuration public class WebMvcConfig extends WebSecurityConfigurerAdapter { @Override public void configure (HttpSecurity http) throws Exception { http.addFilterBefore(new CorsFilter (), WebAsyncManagerIntegrationFilter.class); } }