CORS 是一个 W3C 标准,全称是”跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了 AJAX 只能同源使用的限制(跨域资源共享 CORS 详解)。

解决 CORS 跨域方法大致有如下几类:

  • 使用 Nginx 代理配置转发请求。
  • 在 Zuul (配置允许敏感头信息等) 或  Spring Cloud Gateway 层配置跨域网关路由转发到资源端不涉及跨域。
  • Spring Boot 资源端配置以支持跨域(适用于无网关场景)。

Spring Boot 实现 CORS 跨域 (官方

  • 单个方法的跨域支持,可以使用 @CrossOrigin 的注解实现
  • 采用 JavaConfig 实现
  • 采用 Filter

但是如果项目中包含 Spring Security 就会有 401 的问题,Spring Security 本身是通过 Filter 实现的,如果没有对其单独做 CORS 的处理,在 Web Security 报错 401 的时候是不会返回相应的 CORS 的字段的。这会导致出现的 401 错误成为了一个无法进行跨域的错误,导致前端程序无法正常的处理 401 相应 。对于spring security oauth2 默认接口,例如 /oauth/token 跨域问题,可以通过全局 CORS Filter 解决。

@Configuration
public class GlobalCorsConfiguration { @Bean
public CorsFilter corsFilter() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
// corsConfiguration.addExposedHeader("head1");
//corsConfiguration.addExposedHeader("Location");
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}

配置 CorsFilter 优先级与 Spring Security 不拦截 OPTIONS 请求

  • 配置 Spring Security 策略,不拦截 OPTIONS 请求
  • 自定义 CorsFilter,设置 order 优先级比 Spring Security 的 order 高。

配置服务器允许 /oauth/token的 OPTIONS 方法,因为 /oauth/token 接口是先发一个 OPTIONS 请求,然后再发送 POST请求,如果是 OPTIONS 接口不被允许,就会返回 401 错误。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(-1)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override
protected void configure(HttpSecurity http) throws Exception {
// http.requestMatchers().antMatchers(HttpMethod.OPTIONS, "/oauth/**","/login/**","/logout/**")
// .and()
// .authorizeRequests()
// .antMatchers().permitAll()
// .and()
// .formLogin().permitAll(); //新增login form 支持用户登录及授权 http.requestMatchers().antMatchers(HttpMethod.OPTIONS, "/oauth/**")
.and()
.cors()
.and()
.csrf().disable();
}
}

再次 Vue(vue-resource) 前端测试调用,问题解决。

this.$http.post('http://47.100.188.242:8888/oauth/token',
{'grant_type':'password','scope':'read','username':'test','password':'test'}
,{emulateJSON:true,headers:{Authorization: 'Basic Y2ssxpZW50Xzkd5c3VsuX3dlYjpzwZWNyZXRfOTlezdsW5fMTIzNDU2'}}).then(function(){ }, function(){ });

报文

OPTIONS http://localhost:8888/oauth/token HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: POST
Origin: http://localhost:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36
Access-Control-Request-Headers: authorization
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9 HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:5000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Credentials: true
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Thu, 27 Sep 2018 04:38:56 GMT -------------------------------------------------------------------
POST http://localhost:8888/oauth/token HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Content-Length: 60
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
Origin: http://localhost:5000
Authorization: Basic Y2xpZW50Xzks5c3VuX3dlYjpzZWNyZXRfOsTlzdW5fMTIzNDUs2
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:5000/test.html
Accept-Encoding: gzip, deflate, br grant_type=password&username=test&password=test&soap=api HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:5000
Access-Control-Allow-Credentials: true
Cache-Control: no-store
Pragma: no-cache
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Date: Thu, 27 Sep 2018 04:38:56 GMT
Content-Length: 1560 {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJpcnZpbmciLCJzY29wZSI6WyJhbGwiLCJhcGkiLCJ1c2VyIiwicmVhZCIsIndyaXRlIl0sImV4dF9uYW1lIjoiaXJ2aW5nIiwiZXhwIjoxNTM4MDMwMjk4LCJjbGllbnRfbmFtZSI6ImlydmluZyIsImp0aSI6IjIwOTg1ZjJhLWNiMGUtNDRiZi1hZWIyLTYzMGQ5NWFhNDI3ZSIsImNsaWVudF9pZCI6ImNsaWVudF85OXN1bl93ZWIifQ.koBwYKeLGr0KgZkOAN9ENGtKtHyQnzBRR_b0N1Ck1g2hk5VOGKikBTcCf-HBycHDvFZzADyqzi1640JDWo7MwwZm72r4Ih_QN-A_CztAMLyxsMvotMx9Du4z8wOJ4qaOGEuFGj3HFdFaMG3ltaN1WnoxORolFLqd6O-Q21SUhXaOMldOUZ_AyQildNnJ3-EJSavVSEc78jnW0P5fEJtp7QpRTS6nyvwwQifD7uoshnPWWbAeX7rYfAhEie3m7csx6sB_7nnJavKyk_AUpddioOgvw8QDZSmIGPKDeLlFLNpq1NGssgCqECyxdwmWVCs3x7rr6CwRDFfva87moJQAIQ","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJpcnZpbmciLCJzY29wZSI6WyJhbGwiLCJhcGkiLCJ1c2VyIiwicmVhZCIsIndyaXRlIl0sImF0aSI6IjIwOTg1ZjJhLWNiMGUtNDRiZi1hZWIyLTYzMGQ5NWFhNDI3ZSIsImV4dF9uYW1lIjoiaXJ2aW5nIiwiZXhwIjoxNTM4MDIzMjc1LCJjbGllbnRfbmFtZSI6ImlydmluZyIsImp0aSI6IjRiNTc1MTVlLWEwOTUtNGZiMS05MGQ3LTE0MjQyOTcyMWI1YyIsImNsaWVudF9pZCI6ImNsaWVudF85OXN1bl93ZWIifQ.E8gviZsAb8ci_PMAzj4Oj7bqopr8xNwG3LAwa-pi987yVhg7CTlDhD0QOZLqHVPViwMY7dql-j2ccefwpZsfeaL4i1x5xouANoJ-zRJi7aruxJ_3guy2Ln0fReEYnOnkzKRGjWkdeCbxmrFgg0TkDASB_vTsegsbjqpVfCRg7LAvIcZwQCj1uiSqV8jaHbadvZpA1yZXt7lOMILHwtKkcEOkM7xDUbCHrG1J6qNjRNRbZhI34xXzP0EdXq8_FBA-ykpI1NWTt8jqYtQJfjyZCb9StcQmTqIq234d1ES6uiyZz-pqaiAo7qiVbrK85uOPmuCzuYtZcbWs8neiDeL8_g","expires_in":7161,"scope":"all api user read write","client_name":"irving","ext_name":"irving","jti":"20985f2a-cb0e-44bf-aeb2-630d95aa427e"}

备注:

Spring Security 文档中 (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors)定义的方式。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override
protected void configure(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors().and()
...
} @Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}

Spring Cloud Gateway (http://cloud.spring.io/spring-cloud-gateway/single/spring-cloud-gateway.html#_cors_configuration)

spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "docs.spring.io"
allowedMethods:
- GET

ZUUL

https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.1.0.M3/single/spring-cloud-netflix.html#_enabling_cross_origin_requests

@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/path-1/**")
.allowedOrigins("http://allowed-origin.com")
.allowedMethods("GET", "POST");
}
};
}

REFER:
https://aisensiy.github.io/2017/11/08/spring-cors-and-security/
http://www.spring4all.com/article/177
https://blog.csdn.net/GeorgeShaw1/article/details/75089734
https://stackoverflow.com/questions/37516755/spring-boot-rest-service-options-401-on-oauth-token
https://github.com/pagekit/vue-resource
http://cloud.spring.io/spring-cloud-gateway/single/spring-cloud-gateway.html#_cors_configuration

关于 Spring Security OAuth2 中 CORS 跨域问题的更多相关文章

  1. 关于 Spring Security OAuth2 中 Feign 调用 Token 问题

    微服务体系中,避免不了服务之间链式调用,一般使用 Feign ,由于使用 Spring Security OAuth2 全局做了安全认证,简单的一种实现方式就是在服务提供方获得 Token 再次通过 ...

  2. Spring MVC 4.2 CORS 跨域访问

    跨站 HTTP 请求(Cross-site HTTP request)是指发起请求的资源所在域不同于该请求所指向资源所在的域的 HTTP 请求.比如说,域名A(http://domaina.examp ...

  3. Spring Boot+AngularJS中因为跨域导致Session丢失

    http://blog.csdn.net/dalangzhonghangxing/article/details/52446821 如果还在为跨域问题烦恼,请查看博主的 解决angular+sprin ...

  4. .NET中CORS跨域访问WebApi

    我这里只写基本用法以作记录,具体为什么看下面的文章: http://www.cnblogs.com/landeanfen/p/5177176.html http://www.cnblogs.com/m ...

  5. 禁止chrome中CORS跨域资源共享错误

    在开发中,可以通过命令行命令chrome --allow-file-access-from-files来 禁止CORS错误. 只在紧急情况下使用这个方法,比如你的老板正站在你身后, 并且所有事情都无法 ...

  6. 使用Spring MVC测试Spring Security Oauth2 API

    不是因为看到希望了才去坚持,而坚持了才知道没有希望. 前言 在Spring Security源码分析十一:Spring Security OAuth2整合JWT和Spring Boot 2.0 整合 ...

  7. Spring Boot 中使用 Spring Security, OAuth2 跨域问题 (自己挖的坑)

    使用 Spring Boot 开发 API 使用 Spring Security + OAuth2 + JWT 鉴权,已经在 Controller 配置允许跨域: @RestController @C ...

  8. Spring Boot 2中对于CORS跨域访问的快速支持

    原文:https://www.jianshu.com/p/840b4f83c3b5 目前的程序开发,大部分都采用前后台分离.这样一来,就都会碰到跨域资源共享CORS的问题.Spring Boot 2 ...

  9. CORS跨域请求规则以及在Spring中的实现

    CORS: 通常情况下浏览器禁止AJAX从外部获取资源,因此就衍生了CORS这一标准体系,来实现跨域请求. CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origi ...

随机推荐

  1. PHP 使用数字作为SESSION的Key,一刷新页面session丢失,Session消失,无法存储

    PHP 使用数字作为SESSION的Key,一刷新页面session丢失,Session消失,无法存储 项目中有用到md5截取做session key值的,有些md5截取出来的部分是纯数字的,导致部分 ...

  2. 使用Python完成排序(快排法、归并法)

    class Sort(object): def quick_sort(self, ls): self.quick_sort_helper(ls, 0, len(ls) - 1) return ls d ...

  3. 异步async与await的简单探究

    在学习.net core的过程中,到处见到异步的使用,Task.async.await随处可见.有点疑惑,就去了解了下这个过程是怎样的. 下面是一段代码,去看看是怎么执行的吧. 一.看看异步执行的方式 ...

  4. MyBatis 一级缓存,二级缓存,延迟加载设置

       1  什么是延迟加载  resultMap中的association和collection标签具有延迟加载的功能. 延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息.使用关联信息时再 ...

  5. 20155312 张竞予 Exp6 信息搜集与漏洞扫描

    Exp6 信息搜集与漏洞扫描 目录 基础问题回答 (1)哪些组织负责DNS,IP的管理. (2)什么是3R信息. (3)评价下扫描结果的准确性. 实验总结与体会 实践过程记录 (1)各种搜索技巧的应用 ...

  6. Python类——面向对象

    一.有关面向对象的一些知识 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发“更快更好更强...” ...

  7. rem 原理与简介

    一.rem 原理与简介 字体单位:     值根据 html 根元素大小而定,同样可以作为宽度,高度等单位 适配原理:     将 px 替换为 rem,动态修改 HTML 根元素的 font-siz ...

  8. Qt HID USB通讯错误

    1.下载hidapi库 链接:https://pan.baidu.com/s/1iQBuTxg-fReN-7GTrCT6SA 提取码:xzqw 2.把库加入qt 转自:https://www.cnbl ...

  9. shell遍历文件夹

    遍历目录下的所有文件 假如有一个文件夹路径为dir,遍历文件 for file in /path/dir/* do if test -f $file then echo $file arrary=($ ...

  10. Python 获取车票信息

    提示:该代码仅供学习使用,切勿滥用!!! 先来一个git地址:https://gitee.com/wang_li/li_wang 效果图: 逻辑: 1.获取Json文件的内容 2.根据信息生成URL ...