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,通常是按模块划分,例如一个 ...
随机推荐
- RN集成echarts4图表组件react-native-secharts(转载)
一个webview封装的图表组件.基于百度echarts4,相比native-echarts有echarts自带对象支持,例如渐变色等,用法与官网相同用法. echarts version 4.2.0 ...
- java生成jar并用ikvm生成dll供C#调用
最近想尝试用C#做NB_IOT北向API接口的访问.北向API的接口的访问需要证书的双向认证,而C#不支持双向认证,所以就使用IKVM在C#中跑JAVA程序实现HTTPS请求部分. 步骤如下: 一.使 ...
- 【转载】tolua之wrap文件的原理与使用
什么是wrap文件 每个wrap文件都是对一个c#类的包装,在lua中,通过对wrap类中的函数调用,间接的对c#实例进行操作. wrap类文件生成和使用的总体流程 生成一个wrap文件的流程 这部分 ...
- Masonry学习札记
Masnory学习札记 在之前的文章里有草草提到过Masonry自动布局,可这么重要第三方布局框架的怎么可以怎么随便带过呢!昨天在完成页面的时候刚好遇到了被Masorny功能惊叹的部分,所以趁热打铁写 ...
- Java中switch对整型/字符型/字符串型具体实现细节
转自:http://www.hollischuang.com/archives/61 Java7中switch中支持的数据类型有: byte short int char String类型 其实swi ...
- angularjs - 自定义指令(directive)
自定义指令(directive) 使用 .directive 函数来添加自定义的指令. 要调用自定义指令,HTML 元素上需要添加自定义指令名. 例子:使用驼峰法来命名一个指令, demoDirect ...
- ssh 端口更改或ssh 远程接不上的问题(尤其是国外服务器)
问题: Connecting to 149.*.*.*:22...Connection established.To escape to local shell, press 'Ctrl+Alt+]' ...
- 离线安装Eclipse插件-Vrapper
首先下载Vrapper的资源文件:https://sourceforge.net/projects/vrapper/ 下载完成后解压,将features和plugins文件夹内的文件复制到eclips ...
- linux systemd详解
CentOS 7 使用systemd替换了SysV.Systemd目的是要取代Unix时代以来一直在使用的init系统,兼容SysV和LSB的启动脚本,而且够在进程启动过程中更有效地引导加载服务. s ...
- Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [33,755] milliseconds.
刚部署好程序,第一次登录时,加载非常得慢,查看log日志发现:Creation of SecureRandom instance for session ID generation using [SH ...