webapi+鉴权认证+调用总结
最近项目用到webapi ,参考了网上很多资料,总结了从鉴权到调用、swagger 的注意点。
---------------------------------------------------------------------------------------------------------------------------------
1.Ajax辅助类
/// <summary> /// Ajax请求返回结果 /// </summary> public class AjaxResult { /// <summary> /// 成功失败 /// </summary> public bool status { get; set; } /// <summary> /// 提示消息 /// </summary> public string msg { get; set; } /// <summary> /// Exception消息. /// </summary> public string exmsg { get; set; } /// <summary> /// 状态码 /// </summary> public string statusCode { get; set; } /// <summary> /// 返回数据 /// </summary> public object data { get; set; } public AjaxResult() { } public AjaxResult(bool _status, string _msg, string _exmsg, string _statuscode, object _data) { status = _status; msg = _msg; exmsg = _exmsg; data = _data; statusCode = _statuscode; } public AjaxResult(bool _status, string _msg, string _statuscode) : this(_status, _msg, "", _statuscode, null) { } public AjaxResult(bool _status, string _msg, string _statuscode, object _data) : this(_status, _msg, "", _statuscode, _data) { } public AjaxResult(bool _status, string _statuscode, object _data) : this(_status, "", "", _statuscode, _data) { } }
2.鉴权枚举
public enum StatusCodeEnum { /// <summary> /// 请求(或处理)成功 /// </summary> [Description("请求(或处理)成功")] Success = 200, /// <summary> /// 内部请求出错 /// </summary> [Description("内部请求出错")] Error = 500, /// <summary> /// 未授权标识 /// </summary> [Description("未授权标识")] Unauthorized = 401, /// <summary> /// 请求参数不完整或不正确 /// </summary> [Description("请求参数不完整或不正确")] ParameterError = 400, /// <summary> /// 请求TOKEN失效 /// </summary> [Description("请求TOKEN失效")] TokenInvalid = 403, /// <summary> /// HTTP请求类型不合法 /// </summary> [Description("HTTP请求类型不合法")] HttpMehtodError = 405, /// <summary> /// HTTP请求不合法,请求参数可能被篡改 /// </summary> [Description("HTTP请求不合法,请求参数可能被篡改")] HttpRequestError = 406, /// <summary> /// 该URL已经失效 /// </summary> [Description("该URL已经失效")] URLExpireError = 407 }
3.服务端Filter过滤
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Text; using System.Web; using System.Web.Http.Filters; using System.Web.Mvc; using System.Web.Script.Serialization; using WMS.Server.Common; namespace WMS.Server.Filters { /// <summary> /// /// </summary> public class ApiSecurityFilter : System.Web.Http.Filters.ActionFilterAttribute//注意命名空间Filters { /// <summary> /// 本次不验证token /// </summary> /// <param name="actionContext"></param> public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { AjaxResult resultMsg = null; var request = actionContext.Request; string method = request.Method.Method; string staffid = String.Empty, timestamp = string.Empty, nonce = string.Empty, signature = string.Empty; if (request.Headers.Contains("staffid")) { //当前请求用户账号StaffId staffid = HttpUtility.UrlDecode(request.Headers.GetValues("staffid").FirstOrDefault()); staffid = "mpcheck"; } if (request.Headers.Contains("timestamp")) {//发起请求时的时间戳(单位:毫秒) timestamp = HttpUtility.UrlDecode(request.Headers.GetValues("timestamp").FirstOrDefault()); } if (request.Headers.Contains("nonce")) { //发起请求时的随机数(单位:毫秒) nonce = HttpUtility.UrlDecode(request.Headers.GetValues("nonce").FirstOrDefault()); } if (request.Headers.Contains("signature")) { //当前请求内容的数字签名 signature = HttpUtility.UrlDecode(request.Headers.GetValues("signature").FirstOrDefault()); } //GetToken方法不需要进行签名验证 //if (actionContext.ActionDescriptor.ActionName == "GetToken") //{ //判断请求头是否包含以下参数 if (string.IsNullOrEmpty(staffid) || string.IsNullOrEmpty(timestamp) || string.IsNullOrEmpty(nonce) || string.IsNullOrEmpty(signature)) { resultMsg = new AjaxResult(); resultMsg.statusCode = StatusCodeEnum.ParameterError.GetHashCode().ToString(); resultMsg.exmsg = Utils.GetEnumDescription(StatusCodeEnum.ParameterError); resultMsg.data = ""; resultMsg.status = false; actionContext.Response = toJson(new JsonResult() { Data = resultMsg }); base.OnActionExecuting(actionContext); return; } //} //判断timespan是否有效 double ts1 = 0; double ts2 = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds; bool timespanvalidate = double.TryParse(timestamp, out ts1); double ts = ts2 - ts1; //bool falg = ts > int.Parse(WebSettingsConfig.UrlExpireTime) * 1000;//WebSettingsConfig.UrlExpireTime 配置时间 bool falg = ts > 120 * 1000; if (falg || (!timespanvalidate)) { resultMsg = new AjaxResult(); resultMsg.statusCode = StatusCodeEnum.URLExpireError.GetHashCode().ToString(); resultMsg.exmsg = Utils.GetEnumDescription(StatusCodeEnum.URLExpireError); resultMsg.data = ""; resultMsg.status = false; actionContext.Response = toJson(new JsonResult() { Data = resultMsg }); base.OnActionExecuting(actionContext); return; } //判断token是否有效(token本次不做验证) //Token token = (Token)HttpRuntime.Cache.Get(staffid.ToString()); //string signtoken = string.Empty; //if (HttpRuntime.Cache.Get(id.ToString()) == null) //{ // //resultMsg = new ResultMsg(); // //resultMsg.StatusCode = (int)StatusCodeEnum.TokenInvalid; // //resultMsg.Info = StatusCodeEnum.TokenInvalid.GetEnumText(); // //resultMsg.Data = ""; // //actionContext.Response = HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg)); // base.OnActionExecuting(actionContext); // return; //} //else //{ // signtoken = token.SignToken.ToString(); //} //根据请求类型拼接参数 NameValueCollection form = HttpContext.Current.Request.QueryString; string data = string.Empty; switch (method) { case "POST": Stream stream = HttpContext.Current.Request.InputStream; //注意位置设置 stream.Position = 0; StreamReader streamReader = new StreamReader(stream); data = streamReader.ReadToEnd(); break; case "GET": #region 用于此法,但是cs客户端 ////第一步:取出所有get参数 //IDictionary<string, string> parameters = new Dictionary<string, string>(); //for (int f = 0; f < form.Count; f++) //{ // string key = form.Keys[f]; // if (key != null) // { // parameters.Add(key, form[key]); // } //} //// 第二步:把字典按Key的字母顺序排序 //IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters); //IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator(); //// 第三步:把所有参数名和参数值串在一起 //StringBuilder query = new StringBuilder(); //while (dem.MoveNext()) //{ // string key = dem.Current.Key; // string value = dem.Current.Value; // if (!string.IsNullOrEmpty(key)) // { // query.Append(key).Append(value); // } //} #endregion #region 方式 适用于app端与客户端 StringBuilder query = new StringBuilder(); for (int f = 0; f < form.Count; f++) { string key = form.Keys[f]; if (key != null) { query.Append(key).Append(form[key]); } } #endregion data = query.ToString(); break; default: resultMsg = new AjaxResult(); resultMsg.statusCode = StatusCodeEnum.HttpMehtodError.GetHashCode().ToString(); resultMsg.exmsg = Utils.GetEnumDescription(StatusCodeEnum.HttpMehtodError); resultMsg.data = ""; resultMsg.status = false; actionContext.Response = toJson(new JsonResult() { Data = resultMsg }); base.OnActionExecuting(actionContext); return; } //本次不验证token //bool result = Validate(timestamp, nonce, staffid, signtoken, data, signature); bool result = Validate(timestamp, nonce, staffid, data, signature); if (!result) { resultMsg = new AjaxResult(); resultMsg.statusCode = StatusCodeEnum.HttpRequestError.GetHashCode().ToString(); resultMsg.exmsg = Utils.GetEnumDescription(StatusCodeEnum.HttpRequestError); resultMsg.data = ""; resultMsg.status = false; actionContext.Response = toJson(new JsonResult() { Data = resultMsg }); base.OnActionExecuting(actionContext); return; } else { base.OnActionExecuting(actionContext); } } public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { base.OnActionExecuted(actionExecutedContext); } #region 后续可以提取公用 public bool Validate(string timeStamp, string nonce, string staffId, string data, string signature, string token = "") { var hash = System.Security.Cryptography.MD5.Create(); //拼接签名数据 var signStr = timeStamp + nonce + staffId + data; //将字符串中字符按升序排序 var sortStr = string.Concat(signStr.OrderBy(c => c)); var bytes = Encoding.UTF8.GetBytes(sortStr); //使用MD5加密 var md5Val = hash.ComputeHash(bytes); //把二进制转化为大写的十六进制 StringBuilder result = new StringBuilder(); foreach (var c in md5Val) { result.Append(c.ToString("X2")); } return result.ToString().ToUpper() == signature; } public HttpResponseMessage toJson(Object obj) { String str; if (obj is String || obj is Char) { str = obj.ToString(); } else { JavaScriptSerializer serializer = new JavaScriptSerializer(); str = serializer.Serialize(obj); } HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str, Encoding.GetEncoding("UTF-8"), "application/json") }; return result; } /// <summary> /// 获取枚举的description /// </summary> /// <param name="enumSubitem">枚举值</param> /// <returns>枚举对应的介绍</returns> public static string GetEnumDescription(object enumSubitem) { enumSubitem = (Enum)enumSubitem; string strValue = enumSubitem.ToString(); FieldInfo fieldinfo = enumSubitem.GetType().GetField(strValue); Object[] objs = fieldinfo.GetCustomAttributes(typeof(DescriptionAttribute), false); if (objs == null || objs.Length == 0) { return strValue; } else { DescriptionAttribute da = (DescriptionAttribute)objs[0]; return da.Description; } } #endregion } }
4.服务端api
using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using System.Web.Mvc; using WMS.Server.Common; using WMS.Server.Filters; namespace WMS.Server.Controllers { public class TestController : ApiController { #region 客户端json 请求request.ContentType = "application/json"; //客户端用request.ContentType = "application/json";这种请求,只能传输json格式的参数(JObject obj),但是无法在swagger上显示对应 的参数列表 //[ApiSecurityFilter] [ System.Web.Http.HttpPost] public System.Web.Mvc.JsonResult GetSay(JObject obj) { AjaxResult result = new AjaxResult(); string name = obj["name"] + ""; result.msg = "登录成功!"; var data = new { total = 1, rows = name + "你好" }; result.status = true; result.data = data; return new System.Web.Mvc.JsonResult() { Data = result , JsonRequestBehavior= System.Web.Mvc.JsonRequestBehavior.AllowGet}; } [ApiSecurityFilter] [System.Web.Http.HttpGet] public JsonResult GetSayHI(JObject obj) { AjaxResult result = new AjaxResult(); result.msg = "登录成功!"; string name = obj["name"] + ""; var data = new { total = 1, rows = name + "你好" }; result.status = true; result.data = data; return new JsonResult() { Data = result, JsonRequestBehavior = System.Web.Mvc.JsonRequestBehavior.AllowGet }; } #endregion #region #region 客户端json 请求application/x-www-form-urlencoded;以Diction k/v方式传递参数 //客户端用request.ContentType = "application/x-www-form-urlencoded";这种请求,以Diction k/v方式传递参数能 在swagger上显示对应 的参数列表 。 /// <summary> /// GetSayHIForm /// </summary> /// <param name="name">姓名</param> /// <param name="age">年龄</param> /// <returns></returns> [ApiSecurityFilter] [System.Web.Http.HttpGet] public JsonResult GetSayHIGetForm(string name,string age) { AjaxResult result = new AjaxResult(); result.msg = "登录成功!"; var data = new { total = 1, rows = name + "你好" }; result.status = true; result.data = data; return new JsonResult() { Data = result, JsonRequestBehavior = System.Web.Mvc.JsonRequestBehavior.AllowGet }; } /// <summary> /// GetSayHIForm /// 可以swagger参数显示 /// </summary> /// <param name="name">姓名</param> /// <param name="age">年龄</param> /// <returns></returns> [ApiSecurityFilter] [System.Web.Http.HttpPost] public JsonResult GetSayHIPostForm(string name, string age) { AjaxResult result = new AjaxResult(); result.msg = "登录成功!"; var data = new { total = 1, rows = name + "你好" }; result.status = true; result.data = data; return new JsonResult() { Data = result, JsonRequestBehavior = System.Web.Mvc.JsonRequestBehavior.AllowGet }; } #endregion } }
---------------------------------------------------------------------------------------------------
5.客户端
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Web; namespace webapiClient { public class WebApiHelper { /// <summary> /// Post请求(以Json方式) /// 以Jobject方式请求, egapi: GetSay(Jobject obj) /// </summary> /// <param name="url">请求地址</param> /// <param name="data">请求数据</param> /// <param name="staffId">用户ID</param> /// <returns></returns> public static JObject Post(string url, string data, string staffId = "mpcheck") { return HttpPost(url, data, staffId); } /// <summary> /// Post请求 /// 以 (k/v)方式拼接url可以访问api: GetSay(string name,string age) 和Swagger文档可以具体到参数:注:GET没有这种清空 /// </summary> /// <param name="url">请求地址</param> /// <param name="para">请求数据</param> /// <param name="staffId">用户ID</param> /// <returns></returns> public static JObject Post(string url, Dictionary<string, string> para, string staffId = "mpcheck") { //url参数( 以 (k/v)方式拼接url可以访问api: GetSay(string name,string age) 和Swagger文档可以具体到参数:注:GET没有这种清空 StringBuilder queryurl = new StringBuilder(); //参数加密验证 StringBuilder signquery = new StringBuilder(); foreach (KeyValuePair<string, string> item in para) { queryurl.Append("&").Append(item.Key).Append("=").Append(item.Value); signquery.Append(item.Key).Append(item.Value); } string urlstr = url + (queryurl.ToString().Substring(1, queryurl.Length - 1)); return HttpPost(urlstr, signquery.ToString(), staffId); } /// <summary> /// Post请求 /// </summary> /// <param name="url">地址</param> /// <param name="signparam">传递的参数用于验证</param> /// <param name="staffId">用户ID</param> /// <returns></returns> private static JObject HttpPost(string url, string signparam, string staffId) { byte[] bytes = Encoding.UTF8.GetBytes(signparam); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); //发起请求时的时间戳(单位:毫秒) string timeStamp = GetTimeStamp(); //发起请求时的时间戳(单位:毫秒) string nonce = GetRandom(); //加入头信息 request.Headers.Add("staffid", staffId.ToString()); //当前请求用户StaffId request.Headers.Add("timestamp", timeStamp); request.Headers.Add("nonce", nonce); request.Headers.Add("signature", GetSignature(timeStamp, nonce, staffId, signparam)); //当前请求内容的数字签名 //写数据 request.Method = "POST"; request.ContentLength = bytes.Length; request.ContentType = "application/json"; Stream reqstream = request.GetRequestStream(); reqstream.Write(bytes, 0, bytes.Length); reqstream.Close(); //读数据 request.Timeout = 300000; request.Headers.Set("Pragma", "no-cache"); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream streamReceive = response.GetResponseStream(); StreamReader streamReader = new StreamReader(streamReceive, Encoding.UTF8); string strResult = streamReader.ReadToEnd(); //关闭流 streamReader.Close(); streamReceive.Close(); request.Abort(); response.Close(); return JsonConvert.DeserializeObject<JObject>(strResult); } /// <summary> /// Get请求方式二 复杂验证 适合客户端和app /// </summary> /// <param name="url">请求地址</param> /// <param name="para">请求参数</param> /// <param name="staffId">用户ID</param> /// <returns></returns> public static JObject Get(string url, Dictionary<string, string> para, string staffId = "mpcheck") { Tuple<string, string> tp = GetQueryString(para); //请求参数,用于计算 string query = tp.Item1; //url参数 string queryStr = tp.Item2; string urlstr = url + "?" + queryStr; return HttpGet(urlstr, staffId, query); } /// <summary> /// Get请求方式一简单验证 适合app /// </summary> /// <param name="url">请求地址</param> /// <param name="para">请求参数</param> /// <param name="staffId">用户ID</param> /// <returns></returns> public static JObject Get2(string url, Dictionary<string, string> para, string staffId = "mpcheck") { //url参数 StringBuilder queryurl = new StringBuilder(); //参数加密验证 StringBuilder signquery = new StringBuilder(); foreach (KeyValuePair<string, string> item in para) { queryurl.Append("&").Append(item.Key).Append("=").Append(item.Value); signquery.Append(item.Key).Append(item.Value); } string urlstr = url + "?" + queryurl.ToString(); return HttpGet(url, signquery.ToString(), staffId); } /// <summary> /// HttpGet /// </summary> /// <param name="url">请求地址</param> /// <param name="signparam">参与数字签名的参数</param> /// <param name="staffId">y用户账号</param> /// <returns></returns> private static JObject HttpGet(string url, string signparam, string staffId) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); string timeStamp = GetTimeStamp(); string nonce = GetRandom(); //加入头信息 request.Headers.Add("staffid", staffId.ToString()); //当前请求用户StaffId request.Headers.Add("timestamp", timeStamp); //发起请求时的时间戳(单位:毫秒) request.Headers.Add("nonce", nonce); //发起请求时的时间戳(单位:毫秒) request.Headers.Add("signature", GetSignature(timeStamp, nonce, staffId, signparam)); //当前请求内容的数字签名 request.Method = "GET"; request.ContentType = "application/json"; request.Timeout = 90000; request.Headers.Set("Pragma", "no-cache"); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream streamReceive = response.GetResponseStream(); StreamReader streamReader = new StreamReader(streamReceive, Encoding.UTF8); string strResult = streamReader.ReadToEnd(); streamReader.Close(); streamReceive.Close(); request.Abort(); response.Close(); return JsonConvert.DeserializeObject<JObject>(strResult); } /// <summary> /// 获取token /// </summary> /// <param name="staffId">用户ID</param> /// <returns></returns> private static JObject GetSignToken(string staffId) { //string tokenApi = AppSettingsConfig.GetTokenApi; //Dictionary<string, string> parames = new Dictionary<string, string>(); //parames.Add("staffId", staffId.ToString()); //Tuple<string, string> parameters = GetQueryString(parames); //TokenResultMsg token = WebApiHelper.Get<TokenResultMsg>(tokenApi, parameters.Item1, parameters.Item2, staffId, false); //return token; return new JObject(); } /// <summary> /// 计算签名(没有参与Token) /// </summary> /// <param name="timeStamp">时间戳</param> /// <param name="nonce">随机数</param> /// <param name="staffId">用户ID</param> /// <param name="data">参与计算的请求参数</param> /// <returns></returns> private static string GetSignature(string timeStamp, string nonce, string staffId, string data) { //Token token = null; //var resultMsg = GetSignToken(staffId); //if (resultMsg != null) //{ // if (resultMsg.StatusCode == (int)StatusCodeEnum.Success) // { // token = resultMsg.Result; // } // else // { // throw new Exception(resultMsg.Data.ToString()); // } //} //else //{ // throw new Exception("token为null,员工编号为:" + staffId); //} var hash = System.Security.Cryptography.MD5.Create(); //拼接签名数据 var signStr = timeStamp + nonce + staffId + data; //将字符串中字符按升序排序 var sortStr = string.Concat(signStr.OrderBy(c => c)); var bytes = Encoding.UTF8.GetBytes(sortStr); //使用MD5加密 var md5Val = hash.ComputeHash(bytes); //把二进制转化为大写的十六进制 StringBuilder result = new StringBuilder(); foreach (var c in md5Val) { result.Append(c.ToString("X2")); } return result.ToString().ToUpper(); } /// <summary> /// 获取时间戳 /// </summary> /// <returns></returns> private static string GetTimeStamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalMilliseconds).ToString(); } /// <summary> /// 获取随机数 /// </summary> /// <returns></returns> private static string GetRandom() { Random rd = new Random(DateTime.Now.Millisecond); int i = rd.Next(0, int.MaxValue); return i.ToString(); } /// <summary> /// 拼接get参数(方式二) /// </summary> /// <param name="parames"></param> /// <returns></returns> private static Tuple<string, string> GetQueryString(Dictionary<string, string> parames) { // 第一步:把字典按Key的字母顺序排序 IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parames); IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator(); // 第二步:把所有参数名和参数值串在一起 StringBuilder query = new StringBuilder(""); //签名字符串 StringBuilder queryStr = new StringBuilder(""); //url参数 if (parames == null || parames.Count == 0) return new Tuple<string, string>("", ""); while (dem.MoveNext()) { string key = dem.Current.Key; string value = dem.Current.Value; if (!string.IsNullOrEmpty(key)) { query.Append(key).Append(value); queryStr.Append("&").Append(key).Append("=").Append(value); } } return new Tuple<string, string>(query.ToString(), queryStr.ToString().Substring(1, queryStr.Length - 1)); } } }
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace webapiClient { public partial class WebForm1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } //调用api protected void Unnamed1_Click(object sender, EventArgs e) { Dictionary<string, string> dic = new Dictionary<string, string>(); dic.Add("name", "kinggg"); dic.Add("age", "19"); //////Post json 方式 var postobj = WebApiHelper.Post("http://localhost:8088/Test/GetSay", JsonConvert.SerializeObject(dic)); //////Get1 json方式//适合app端 // var getobj1 = WebApiHelper.Get("http://localhost:8088/Test/GetSayHI", dic); ////Get2 json 比Get1只是多重验证。//适合客户端 var getobj2 = WebApiHelper.Get("http://localhost:8088/Test/GetSayHIGetForm", dic); //---请求contenttype=application/x-www-form-urlencoded 的k/v方式在swagger文档可以显示每个参数 //Get k/v 方式 //var getkv2 = WebApiHelper.Get("http://localhost:8088/Test/GetSayHIGetForm", dic); //post k/v 方式 var postkv2 = WebApiHelper.Post("http://localhost:8088/Test/GetSayHIPostForm", dic); } } }
----------------------------------------------------------------------------------------------------------
//1.注意Post方式请求时,如果要想在Swagger分别显示每个参数,需要在post方式创建url时,拼接参数

风雨之后见彩虹

浙公网安备 33010602011771号