当一个客户端去访问另一个不同域名或者同域名不同端口的服务时,就会发出跨域请求。如果此时该服务不允许其进行跨域资源访问,就会因为跨域问题而导致访问失败。跨源域资源共享CORS(Cross-Origin Resource Sharing)允许Web应用服务器进行跨域访问控制。本文介绍如何在ASM的Virtualservice中配置corsPolicy,实现跨域访问。
跨源域资源共享CORS介绍
浏览器会限制页面脚本内发起的跨源HTTP请求以提高安全性,对于需要跨域访问的场景,ASM提供了跨源域资源共享CORS功能。跨域资源共享CORS是一种基于HTTP头的机制,该机制允许服务器标示除了它自己以外的其它域,协议和端口,使得浏览器可以访问加载这些资源。
跨域资源共享CORS的验证机制分两种模式:简单请求和预先请求。
- 简单请求模式:
浏览器直接发送跨域请求,并在请求头中携带Origin的头,表明这是一个跨域的请求。服务器端接到请求后,会根据自己的跨域规则,通过Access-Control-Allow-Origin和Access-Control-Allow-Methods响应头来返回验证结果。
- 预先请求模式:
浏览器会先发送Preflighted requests(预先验证请求),Preflighted requests是一个OPTION请求,用于询问要被跨域访问的服务器,是否允许当前域名下的页面发送跨域的请求。在得到服务器的跨域授权后才能发送真正的HTTP请求。
OPTIONS请求头部中会包含以下头部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers。服务器收到OPTIONS请求后,设置Access-Control-Allow-Origin、Access-Control-Allow-Method、Access-Control-Allow-Headers、Access-Control-Max-Age头部与浏览器沟通来判断是否允许这个请求。如果Preflighted requests验证通过,浏览器才会发送真正的跨域请求。
当请求同时满足下面三个条件时,CORS验证机制会使用简单请求模式进行处理,否则CORS验证机制会使用预先请求模式进行处理。
在VirtualService中配置corsPolicy
整个CORS通信过程都是浏览器自动完成,您需要在对应服务的VirtualService中添加
corsPolicy
字段,使服务允许跨域请求,从而实现跨域通信。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings-route
spec:
hosts:
- ratings.prod.svc.cluster.local
http:
- route:
- destination:
host: ratings.prod.svc.cluster.local
subset: v1
corsPolicy:
allowOrigins:
- exact: https://example.com
- regex: .* #支持regex。
allowMethods:
- POST
- GET
allowCredentials: false
allowHeaders:
- X-Foo-Bar
maxAge: "24h"
参数 | 说明 |
---|
allowOrigins | 允许请求服务的来源,允许带哪些Origin地址的请求,支持regex匹配。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。 |
allowMethods | 允许请求服务的方法,实际请求所允许使用的HTTP方法。 |
allowHeaders | 允许请求服务的标头,用于预检请求的响应。其指明了实际请求中允许携带的首部字段。 |
exposeHeaders | 公开请求服务的标头,让服务器把允许浏览器访问的头放入白名单。 |
maxAge | 最大浏览器缓存时间,指定了浏览器能够缓存preflight请求结果的时间。 |
allowCredentials | 允许请求服务的凭证,符合要求的凭证才能请求服务。 |
跨域访问最佳实践
准备工作
步骤一:部署应用
- 部署后端应用。
- 通过kubectl工具连接集群。
- 使用以下内容,创建details.yaml 文件。
apiVersion: v1
kind: Service
metadata:
name: details
labels:
app: details
service: details
spec:
ports:
- port: 9080
name: http
selector:
app: details
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-details
labels:
account: details
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: details-v1
labels:
app: details
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: details
version: v1
template:
metadata:
labels:
app: details
version: v1
spec:
serviceAccountName: bookinfo-details
containers:
- name: details
image: docker.io/istio/examples-bookinfo-details-v1:1.16.4
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
securityContext:
runAsUser: 1000
- 执行以下命令,在default命名空间部署details应用。
kubectl apply -f details.yaml -n default
- 部署前端应用。
- 使用以下内容,创建istio-cors-demo.yaml 文件。
apiVersion: v1
kind: ServiceAccount
metadata:
name: istio-cors-demo
---
apiVersion: v1
kind: Service
metadata:
name: istio-cors-demo
labels:
app: istio-cors-demo
service: istio-cors-demo
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: istio-cors-demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: istio-cors-demo
spec:
replicas: 1
selector:
matchLabels:
app: istio-cors-demo
version: v1
template:
metadata:
labels:
app: istio-cors-demo
version: v1
spec:
serviceAccountName: istio-cors-demo
containers:
- image: registry.cn-hangzhou.aliyuncs.com/build-test/istio-cors-demo:v1.0-g8e215f6-aliyun
imagePullPolicy: IfNotPresent
name: istio-cors-demo
ports:
- containerPort: 80
- 执行以下命令,在foo命名空间部署istio-cors-demo应用。
kubectl apply -f istio-cors-demo.yaml -n foo
步骤二:创建ASM网关
- 登录ASM控制台,在左侧导航栏,选择。
- 在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择。
- 在入口网关页面,单击创建。
- 在创建页面,设置名称为ingressgateway,选择部署集群,设置负载均衡类型为公网访问,在新建负载均衡下选择负载均衡规格,其他参数采用默认设置,然后单击创建。
- 重复步骤3和步骤4,创建名为ingressgateway2的网关。
步骤三:创建路由规则
- 创建后端应用的路由规则。
- 创建网关规则,绑定details应用和ingressgateway网关。
- 登录ASM控制台,在左侧导航栏,选择。
- 在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择。
- 在网关规则页面,单击使用YAML创建。
- 在创建页面,设置命名空间为default,选择任意场景模版,将YAML文本框中的内容替换为以下内容,然后单击创建。
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: bookinfo-gateway
namespace: default
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- '*'
port:
name: http
number: 80
protocol: HTTP
- 创建虚拟服务。
- 在网格详情页面左侧导航栏选择,然后在右侧页面单击使用YAML创建。
- 在创建页面,设置命名空间为default,选择任意场景模版,将YAML文本框中的内容替换为以下内容,然后单击创建。
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: bookinfo
namespace: default
spec:
gateways:
- bookinfo-gateway
hosts:
- '*'
http:
- match:
- uri:
prefix: /details
route:
- destination:
host: details
port:
number: 9080
- 访问后端应用。
- 获取ingressgateway网关的IP地址,具体操作,请参见创建入口网关服务。
- 在浏览器地址栏中输入http://<ingressgateway网关的IP>/details/2 。

返回以上页面,表明访问后端应用details成功。
- 创建前端应用的路由规则。
- 创建网关规则。
创建网关规则,绑定istio-cors-demo应用和ingressgateway2网关。
- 在网格详情页面左侧导航栏,选择,然后在网关规则页面,单击使用YAML创建。
- 在创建页面,设置命名空间为foo,选择任意场景模版,将YAML文本框中的内容替换为以下内容,然后单击创建。
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: istio-cors-demo-gateway
namespace: foo
spec:
selector:
istio: ingressgateway2
servers:
- hosts:
- '*'
port:
name: http
number: 80
protocol: HTTP
- 创建虚拟服务。
- 在网格详情页面左侧导航栏,选择,然后在虚拟服务页面,单击使用YAML创建。
- 在创建页面设置命名空间为foo,选择任意场景模版,将YAML文本框中的内容替换为以下内容,然后单击创建。
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: istio-cors-demo
namespace: foo
spec:
gateways:
- istio-cors-demo-gateway
hosts:
- '*'
http:
- route:
- destination:
host: istio-cors-demo
port:
number: 8000
- 使用前端应用访问后端应用。
- 获取ingressgateway2网关的IP地址,具体操作,请参见创建入口网关服务。
- 在谷歌浏览器地址栏,输入http://<ingressgateway2网关的IP地址>。
- 在URL文本框,输入http://<ingressgateway网关的IP地址>/details/2 ,单击Send。
- 在谷歌浏览器右上角,单击
图标,选择。
由于前端istio-cors-demo应用访问后端details应用需要跨域,访问会失败,因此出现以上报错。
步骤四:配置跨域访问
- 登录ASM控制台,在左侧导航栏,选择。
- 在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择。
- 在虚拟服务页面,单击bookinfo右侧操作列下的查看YAML。
- 在编辑对话框的http参数下,添加以下跨域配置,然后单击确定。
- corsPolicy:
allowCredentials: false
allowMethods:
- POST
- GET
allowOrigins:
- prefix: 'http://<ingressgateway2网关的IP>'
maxAge: 24h

步骤五:验证跨域访问是否成功
- 在谷歌浏览器地址栏,输入http://<ingressgateway2网关的IP地址>。
- 在URL文本框,输入http://<ingressgateway网关的IP地址>/details/2 ,单击Send。

返回以上页面,表明前端istio-cors-demo应用访问后端details应用成功,跨域访问成功。