最近被跨域问题搞得焦头烂额,在前端ajax往服务器端发送请求的时候,我尝试在请求报文header里面自定义请求头:
$.ajax({
type: "GET",
url: 'http://localhost:49420/api/v1/User/GetById?id=1',
dataType: "json",
headers: {
AppKey: "fasdf2236afasdZ98",
Sign: "6930718a6a0395349c20b9b409a52c72"
},
success: function(data, status) {},
error: function(error) {}
});
后端服务器:
IEnumerable<string> appKeys;
if (!actionContext.Request.Headers.TryGetValues("AppKey",out appKeys))
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized)
{
Content = new StringContent("报文头中的AppKey为空")
};
}
可是本来以为这样写没问题,可是浏览器端一直给我返401:Unauthorized错误,并且提示 已拦截跨源请求:同源策略禁止读取远程资源。(原因:CORS 请求未能成功)
各种方法尝试了,未果。。。。
之后发现是服务器端未验证通过客户端的methods,需要在AuthorizationFilter过滤器的最开始,验证请求报文的方法是不是options,如果不是直接打回,是的话返回200:Accepted,然后进行后面AuthorizationFilter的验证。
string method = actionContext.Request.Method.ToString();
if (method == "OPTIONS")
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
}
注意:必须给浏览器返回一个200码,才能进行自定义头部信息的验证
※另外:必须在配置文件中"Access-Control-Allow-Headers"的值中加上“appkey,sign”。表示允许接收报文头部的appkey,sign。不设置的话报以下错误
已拦截跨源请求:同源策略禁止读取位于 ****的远程资源。(原因:来自 CORS 预检通道的 CORS 头 ‘Access-Control-Allow-Headers’ 的令牌 ‘appkey’ 无效)。
总之,对症下药就好了~~
最后附上AuthorizationFilter过滤器的完整代码和我的解决跨域问题配置文件这一块的代码,仅供参考。
using RuPeng.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using UserCenter.IServices;
namespace UserCenter.OpenAPI.Filters
{
public class UCAuthorizationFilter : IAuthorizationFilter
{
//一个对象必须是IOC容器创建出来的,才能注入
public IAppInfoService appInfoService { get; set; }
public bool AllowMultiple => true;
public async Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
string method = actionContext.Request.Method.ToString();
if (method == "OPTIONS")
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
}
//获得报文头中的AppKey和Sign
IEnumerable<string> appKeys;
if (!actionContext.Request.Headers.TryGetValues("AppKey",out appKeys))
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized)
{
Content = new StringContent("报文头中的AppKey为空")
};
}
IEnumerable<string> signs;
if (!actionContext.Request.Headers.TryGetValues("Sign", out signs))
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized)
{
Content = new StringContent("报文头中的Sign为空")
};
}
string appKey = appKeys.First();
string sign = signs.First();
var appInfo = await appInfoService.GetByAppKeyAsync(appKey);
if (appInfo == null)
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized)
{
Content = new StringContent("不存在的AppKey")
};
}
if (!appInfo.IsEnabled)
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
Content = new StringContent("AppKey已经被封禁")
};
}
//计算用户输入参数的连接+AppSecret的Md5值
//orderedQS就是按照key(参数的名字)进行排序的QueryString集合
var orderedQS = actionContext.Request.GetQueryNameValuePairs().OrderBy(kv => kv.Key);
var segments = orderedQS.Select(kv => kv.Key + "=" + kv.Value); //拼接key=value的数组
string qs = string.Join("&", segments); //用&符号拼接起来
string computedSign = MD5Helper.ComputeMd5(qs + appInfo.AppSecret);//计算qs+secret 的md5值
//用户传进来md5值和计算出来的比对一下,就知道数据是否有被篡改过
if (sign.Equals(computedSign,StringComparison.CurrentCultureIgnoreCase))
{
return await continuation();
}
else
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized)
{ Content = new StringContent("sign验证失败") };
}
}
}
}
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Content-Type" value="application/json"/>
<add name="Access-Control-Allow-Headers" value=" Content-Type,X-Requested-With,appkey,sign" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>