Origin中会指出当前请求属于哪个域(协议+域名+端口)。服务会根据这个值决定是否允许其跨域。
如果服务器允许跨域,需要在返回的响应头中携带下面信息:
Access-Control-Allow-Origin: http://manage.leyou.com
Access-Control-Allow-Credentials: true
Content-Type: text/html; charset=utf-8
有关cookie:
要想操作cookie,需要满足3个条件:
响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名
不符合简单请求的条件,会被浏览器判定为特殊请求,,例如请求方式为PUT。
预检请求
特殊请求会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest
请求,否则就报错。
一个“预检”请求的样板:
OPTIONS /cors HTTP/1.1
Origin: http://manage.leyou.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.leyou.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
与简单请求相比,除了Origin以外,多了两个头:
预检请求的响应
服务的收到预检请求,如果许可跨域,会发出响应:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://manage.leyou.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Max-Age: 1728000
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
除了Access-Control-Allow-Origin
和Access-Control-Allow-Credentials
以外,这里又额外多出3个头:
如果浏览器得到上述响应,则认定为可以跨域,后续就跟简单请求的处理是一样的了。
虽然原理比较复杂,但是前面说过:
事实上,SpringMVC已经帮我们写好了CORS的跨域过滤器:CorsFilter ,内部已经实现了刚才所讲的判定逻辑,我们直接用就好了。
在leyou-gateway
中编写一个配置类,并且注册CorsFilter。
描述一下我的项目结构和请求流程:
前台发起访问http://api.leyou.com/api/item/category/list?pid=0
经过nginx,判断资源定向
于是发送给ly-api-gateway
去处理,这个时候就需要在ly-api-gateway
当中配置CorsFilter
了,由于是http://manage.leyou.com
发送到api.leyou.com
,这就涉及到服务端对特定域下的资源请求是否通过了,我们在下面代码当中添加了允许的域:configuration.addAllowedOrigin("http://manage.leyou.com");
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter(){
//添加cors配置信息
CorsConfiguration configuration=new CorsConfiguration();
//1) 允许的域,不要写*,否则cookie就无法使用了
configuration.addAllowedOrigin("http://manage.leyou.com");
//2) 是否发送Cookie信息
configuration.setAllowCredentials(true);
//3) 允许的请求方式
configuration.addAllowedMethod("OPTIONS");
configuration.addAllowedMethod("HEAD");
configuration.addAllowedMethod("GET");
configuration.addAllowedMethod("PUT");
configuration.addAllowedMethod("POST");
configuration.addAllowedMethod("DELETE");
configuration.addAllowedMethod("PATCH");
// 4)允许的头信息
configuration.addAllowedHeader("*");
//2.添加映射路径,我们拦截一切请求
UrlBasedCorsConfigurationSource configurationSource=new UrlBasedCorsConfigurationSource();
configurationSource.registerCorsConfiguration("/**",configuration);
//返回新的corsFilter
return new CorsFilter(configurationSource);
}
}
紧接着网关识别出当前资源请求域是http://manage.leyou.com
,于是给予处理,发现匹配模式/api
,与自己配置文件吻合,于是路由到item-service
这个微服务去处理。
最后即是在item-service
当中做处理并返回结果
@Controller
@RequestMapping("category")
public class CategoryController {
@Autowired
CategoryService categoryService;
@RequestMapping("list")
public ResponseEntity<List<Category>> queryCategoryListByParentId(@RequestParam(value = "pid",defaultValue = "0")Long pid){
try {
if(pid==null||pid.longValue()<0){
// pid为null或者小于等于0,响应400
return ResponseEntity.badRequest().build();
}
//执行查询操作
List<Category> categoryList=this.categoryService.queryCategoryListByParentId(pid);
if(categoryList==null||categoryList.size()<1){
// 返回结果集为空,响应404
return ResponseEntity.notFound().build();
}
//响应200
return ResponseEntity.ok(categoryList);
}catch (Exception e){
e.printStackTrace();
}
//以上都不是,响应500
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
这里自己犯二了,之前明显配置的是允许http://manage.leyou.com
域的请求,而自己在启动Vue后台项目之后,使用它的默认配置请求地址 http://localhost:9001
,请求发现还是跨域的问题,检查两遍后改了请求域名就好了。