Web API的接口访问安全性
使用签名获取Token
首先我们自定义appkey、appSecret。可用GUID随机生成,AppSecret要不定期更换。然后放到配置文件中。
Appkey=1AF62C68-B970-46E7-B545-E5A5712249D4
AppSecret=DA2502F8-626A-405D-90DB-0351A086FE49
WebApI端MD5签名
public class AuthorizationHelper
{
public static bool CheckPartner(HttpRequestBase request, string appSecret,out string msg)
{
NameValueCollection getCollection = request.Params;//此签名要求Partner及Sign均通过QueryString传递
if (getCollection == null || getCollection.Count == )
{
msg = "调用失败";
return false;
}
string appkey = getCollection["appkey"];
string sign = getCollection["sign"];
string timestamp = getCollection["timestamp"];
bool valid=!string.IsNullOrWhiteSpace(appkey)//必须包含partner
&& !string.IsNullOrWhiteSpace(sign)//必须包含sign
&& !string.IsNullOrWhiteSpace(timestamp)//必须包含timestamp
&& Regex.IsMatch(appkey, "^[0-9A-Za-z]{32}$")//appkey必须为32位
&& Regex.IsMatch(timestamp, "^(((20[0-3][0-9]-(0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|(20[0-3][0-9]-(0[2469]|11)-(0[1-9]|[12][0-9]|30))) (20|21|22|23|[0-1][0-9]):[0-5][0-9]:[0-5][0-9])$")//timestamp格式必须是yyyy-MM-dd hh:mm:ss
&& Regex.IsMatch(sign, "^[0-9A-Za-z]{32}$");//sign必须为32位Md5摘要 if (valid)
{
if (!string.IsNullOrWhiteSpace(appSecret))
{
//根据请求数据获取MD5签名
string vSign = Util.Md5(appkey, appSecret, timestamp);
if (string.Equals(sign, vSign, StringComparison.OrdinalIgnoreCase))
{ var requestDatetime=Convert.ToDateTime(timestamp);
var span= DateTime.Now.Subtract(requestDatetime);
if (Math.Abs(span.TotalMinutes) > )//超过20分钟的请求就过期
{
msg="非法调用,请求过期!";
return false;
}
msg = "调用成功";
return true;
}
}
}
msg = "调用失败";
return false;
} public static bool CheckPartner(string appkey,string sign,string timestamp, string appSecret, out string msg)
{
bool valid = !string.IsNullOrWhiteSpace(appkey)//必须包含partner
&& !string.IsNullOrWhiteSpace(sign)//必须包含sign
&& !string.IsNullOrWhiteSpace(timestamp)//必须包含timestamp
&& Regex.IsMatch(appkey, "^[0-9A-Za-z]{32}$")//appkey必须为32位
&& Regex.IsMatch(timestamp, "^(((20[0-3][0-9]-(0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|(20[0-3][0-9]-(0[2469]|11)-(0[1-9]|[12][0-9]|30))) (20|21|22|23|[0-1][0-9]):[0-5][0-9]:[0-5][0-9])$")//timestamp格式必须是yyyy-MM-dd hh:mm:ss
&& Regex.IsMatch(sign, "^[0-9A-Za-z]{32}$");//sign必须为32位Md5摘要 if (valid)
{ if (!string.IsNullOrWhiteSpace(appSecret))
{
//根据请求数据获取MD5签名
string vSign = Util.Md5(appkey, appSecret, timestamp);
if (string.Equals(sign, vSign, StringComparison.OrdinalIgnoreCase))
{
var requestDatetime=Convert.ToDateTime(timestamp);
var span= DateTime.Now.Subtract(requestDatetime);
if (Math.Abs(span.TotalMinutes) > )//超过10分钟的请求就过期
{
msg="非法调用,请求过期!";
return false;
}
msg = "调用成功";
return true;
}
}
}
msg = "调用失败";
return false;
}
}
WebApi端生成token
public class BasePrincipal
{
/// <summary>
/// 用户Id
/// </summary>
public int Id { get; set; }
/// <summary>
/// 手机号
/// </summary>
public string Phone { get; set; }
/// <summary>
/// token
/// </summary>
public string Token { get; set; }
/// <summary>
/// 有效期(秒)
/// </summary>
public int Expires { get; set; }
/// <summary>
/// 消息
/// </summary>
public string Message { get; set; } } public class HomeController : Controller
{
/// <summary>
/// Redis帮助类
/// </summary>
static readonly RedisHelper redisHelper = new RedisHelper(); [AllowAnonymous]
public JsonResult Login()
{
BasePrincipal result = new BasePrincipal();
string token = Request.Headers["token"];
var key = "Login:token:";
if (!string.IsNullOrEmpty(token)) //有token,取redis
{
key=$"{key}{token}";
result = redisHelper.StringGet<BasePrincipal>(key);
if (result != null)
{
return new JsonResult() { Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
} var phone = Request.Params["phone"];
if (phone == null)
{
result.Message = "缺少手机号";
return new JsonResult(){Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
} var msg = "";
var AppSecret = System.Configuration.ConfigurationManager.AppSettings["AppSecret"];
var isAuthorization = AuthorizationHelper.CheckPartner(Request, AppSecret, out msg);
if (!isAuthorization)
{
result.Message = $"检验不成功,{0},msg";
return new JsonResult(){Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
//获取用户信息业务,各位根据业自己实现这行代码
result = personService.LoginPersonInfo(phone);
if (result == null)
{
result.Message = "用户不存在";
return new JsonResult(){Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
} token = Guid.NewGuid().ToString("N");//生成token
result.Token = token;
result.Expires = * * ;
//设置redis
key = $"{key}{token}";
redisHelper.StringSet(key, result,TimeSpan.FromSeconds(result.Expires));
return new JsonResult(){Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; }
}
WebApi在Global.asax.cs端校验token
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles); } void Application_BeginRequest(object sender, EventArgs e)
{
//Cors跨域设置(这些配制放在HttpMethod=="OPTIONS"里面就调用出错,放出来就没事,不知原因)
var response = HttpContext.Current.Response;
response.AddHeader("Access-Control-Allow-Origin", "*"); //正式环境注意改成具体网站,*代表允许所有网站
response.AddHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
response.AddHeader("Access-Control-Allow-Headers", "Content-Type,token,Authorization");//Content-Type
response.AddHeader("Access-Control-Max-Age", "");//设置跨域缓存,减少浏览器OPTIONS访问次数 if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
response.End();
}
} protected void Application_Error(object sender, EventArgs e)
{
//获取到HttpUnhandledException异常,这个异常包含一个实际出现的异常
Exception ex = Server.GetLastError();
//实际发生的异常
Exception innerException = ex.InnerException;
if (innerException != null) ex = innerException; HttpContext.Current.Response.Write(string.Format("捕捉到未处理的异常:{0}<br/>", ex.GetType().ToString()));
HttpContext.Current.Response.Write("Global已进行错误处理。<br/>");
HttpContext.Current.Response.Write(string.Format("Exception:{0}", ex)); Server.ClearError();
} void Application_PostAuthenticateRequest(object sender, EventArgs e)
{ // 如果是header中token认证
if (Request.Headers["token"] != null)
{
var token = Request.Headers["token"];
if (!string.IsNullOrEmpty(token))
{
var key = $"Login:token:{token}";
RedisHelper redisHelper = new RedisHelper();
var user = redisHelper.StringGet<BasePrincipal>(key);
if (user != null)
{
HttpContext.Current.User = new PersonPrincipal(user);
return;
}
}
} HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName]; if (authCookie != null)
{
var url = HttpContext.Current.Request.Url.ToString();
if (!string.IsNullOrEmpty(url) && url.StartsWith("https"))
{
authCookie.Secure = true;
}
BasePrincipal clientUserData = null;
try
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket != null)
clientUserData = JsonConvert.DeserializeObject<BasePrincipal>(authTicket.UserData);
}
catch
{
// ignored
} if (HttpContext.Current != null && clientUserData != null)
{
HttpContext.Current.User = new PersonPrincipal(clientUserData);
}
}
} protected void Application_End()
{
} }
WebApi 配置文件中加入authentication等节点
<system.web>
<authentication mode="Forms">
<forms name=".testToken" loginUrl="url" timeout="" protection="All" defaultUrl="index.html" />
</authentication>
<compilation debug="true" targetFramework="4.6.2" />
<httpRuntime targetFramework="4.5" maxRequestLength="" executionTimeout="" />
<customErrors mode="Off" />
<globalization culture="zh-cn" uiCulture="zh-CHS" />
<sessionState mode="Off"></sessionState>
<httpCookies httpOnlyCookies="true" requireSSL="true" />
</system.web>
客户端签名后调用WebApi Login方法获取token
public BasePrincipal GetToken(string phone)
{
var appkey = System.Configuration.ConfigurationManager.AppSettings["Appkey"];
var appSecret = System.Configuration.ConfigurationManager.AppSettings["AppSecret"];
var webApi_url = System.Configuration.ConfigurationManager.AppSettings["WebApi_url"]; var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); #region sign
string[] inputs = new string[] { appkey, appSecret, timestamp };
Array.Sort(inputs);//排序
string tmpStr = string.Join("", inputs);
var md5Hash = new System.Security.Cryptography.MD5CryptoServiceProvider();
var data = md5Hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(tmpStr));
var sBuilder = new System.Text.StringBuilder();
foreach (var t in data)
{
sBuilder.Append(t.ToString("x2"));
} var sign = sBuilder.ToString();
#endregion var url = string.Format(webApi_url + "/Home/Login?appkey={0}×tamp={1}&sign={2}&phone={4}", appkey, timestamp, sign, phone); var result = HttpClientManager.GetResponse<BasePrincipal>(url); if (result != null)
{
return result;
} return null;
}
将获取到的token保存在客户端中,在调用webapi接口时,把token放到header中
function GetPerson(phone) {
let token = localStorage.getItem("token");
$.ajax({
url: 'http://*******/api/Person/GetPerson,
data: {
phone: phone
},
beforeSend: function (request) {
request.setRequestHeader("token", token);
},
dataType: 'JSON',
async: false,//请求是否异步,默认为异步
type: 'GET',
success: function (list) {
},
error: function () {
}
});
}
Web API的接口访问安全性的更多相关文章
- 基于ASP.NET WEB API实现分布式数据访问中间层(提供对数据库的CRUD)
一些小的C/S项目(winform.WPF等),因需要访问操作数据库,但又不能把DB连接配置在客户端上,原因有很多,可能是DB连接无法直接访问,或客户端不想安装各种DB访问组件,或DB连接不想暴露在客 ...
- Web Api跨域访问配置及调用示例
1.Web Api跨域访问配置. 在Web.config中的system.webServer内添加以下代码: <httpProtocol> <customHeaders> &l ...
- 适用于app.config与web.config的ConfigUtil读写工具类 基于MongoDb官方C#驱动封装MongoDbCsharpHelper类(CRUD类) 基于ASP.NET WEB API实现分布式数据访问中间层(提供对数据库的CRUD) C# 实现AOP 的几种常见方式
适用于app.config与web.config的ConfigUtil读写工具类 之前文章:<两种读写配置文件的方案(app.config与web.config通用)>,现在重新整理一 ...
- ASP.NET Web API 跨域访问(CORS)
一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...
- Web Api 2 接口API文档美化
使用用第三方提供的swgger ui 帮助提高 web api 接口列表的阅读性,并且可以在页面中测试服务接口. 运行程序如下: 注意:在IE中必须输入红色部分. 并且可以对方法进行测试. 在开发we ...
- 使用swagger实现web api在线接口文档
一.前言 通常我们的项目会包含许多对外的接口,这些接口都需要文档化,标准的接口描述文档需要描述接口的地址.参数.返回值.备注等等:像我们以前的做法是写在word/excel,通常是按模块划分,例如一个 ...
- .NET Core WEB API中接口参数的模型绑定的理解
在.NET Core WEB API中参数的模型绑定方式有以下表格中的几种: 微软官方文档说明地址:https://docs.microsoft.com/zh-cn/aspnet/core/web-a ...
- ASP.NET Web Api 2 接口API文档美化之Swagger
使用第三方提供的swgger ui 可有效提高 web api 接口列表的阅读性,并且可以在页面中测试服务接口. 但本人在查阅大量资料并进行编码测试后,发现大部分的swagger实例并不能有效运行.例 ...
- 使用swagger实现web api在线接口文档(转载)
一.前言 通常我们的项目会包含许多对外的接口,这些接口都需要文档化,标准的接口描述文档需要描述接口的地址.参数.返回值.备注等等:像我们以前的做法是写在word/excel,通常是按模块划分,例如一个 ...
随机推荐
- 常用css
边框 css 基本设置:border:1px solid #d2d2d2; 风格有:solid=>实线 double=>双实线 dotted=>点状 dashed=> ...
- vue打包上传oss
今天把vue打包之后上传到oss,遇到了一点问题,现在解决了总结一下心得: OSS (Object Storage Service)名为对象存储,配合cdn使用达到静态文件托管加速,提升网站文件访问速 ...
- python 7
一.数据类型的补充 1.元组 tu1 = ('大海') tu2 = ('大海',) print(tu1, type(tu1), tu2, type(tu2)) 大海 <class 'str'&g ...
- 百战程序员——JSP
JSP全称是什么?它相比servlet有什么优势?有什么劣势? JSP全名为Java Server Pages,中文名叫java服务器页面,其根本是一个简化的Servlet设计. JSP技术使用Jav ...
- Django_ORM相关操作
一般的操作 1.all():查询所有的结果 2.filter():包含与所有筛选条件匹配的对象 3.get():返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果对象没有或者超过一个会报错 4 ...
- PA教材提纲 TAW10-1
Unit1 SAP systems(SAP系统) 1.1 Explain the Key Capabilities of SAP NetWeaver(解释SAP NetWeaver的关键能力) Rep ...
- linux下lampp的启动和停止脚本
今天试着写了lampp的启动停止脚本,和上一篇的tomcat的启动停止有一点小区别,就是lampp启动之后有很多的进程号,如果按照tomcat的停止脚本写就会出错,下面做细细的介绍 1.lampp的停 ...
- Python3常用函数、方法总结(持续更新…)
最近刷LeetCode,自己自娱自乐完之后去discussion看大佬们的各种巧妙解法,总是止不住的双击666--加上最近Python3用的比较多(虽然Python实在不推荐跑算法题目,一是运行效率太 ...
- 使用JBolt新建Maven版工程步骤
一.打开新建对话框 在左侧右键new中可以找到JFinal创建工程的菜单 JBoltHome页面也有快捷按钮用来弹出创建工程对话框. 二.填写Maven和其他信息配置 填写工程name 主包名 下面有 ...
- Python练习九
1.处理文件,用户指定要查找的文件和内容,将文件中包含要查找内容的每一行都输出到屏幕. def check_file(filename, content): with open(filename, e ...