当前位置: 首页 > 编程日记 > 正文

【WEB API项目实战干货系列】- API登录与身份验证(三)

上一篇: 【WEB API项目实战干货系列】- 接口文档与在线测试(二)

这篇我们主要来介绍我们如何在API项目中完成API的登录及身份认证. 所以这篇会分为两部分, 登录API, API身份验证.

这一篇的主要原理是: API会提供一个单独的登录API, 通过用户名,密码来产生一个SessionKey, SessionKey具有过期时间的特点, 系统会记录这个SessionKey, 在后续的每次的API返回的时候,客户端需带上这个Sessionkey, API端会验证这个SessionKey.

登录API

我们先来看一下登录API的方法签名

image

SessionObject是登录之后,给客户端传回的对象, 里面包含了SessionKey及当前登录的用户的信息

image

这里每次的API调用,都需要传SessionKey过去, SessionKey代表了用户的身份信息,及登录过期信息。

登录阶段生成的SessionKey我们需要做保存,存储到一个叫做UserDevice的对象里面, 从语意上可以知道用户通过不同的设备登录会产生不同的UserDevice对象.

image

最终的登录代码如下:

复制代码
[RoutePrefix("api/accounts")]public class AccountController : ApiController{private readonly IAuthenticationService _authenticationService = null;public AccountController(){//this._authenticationService = IocManager.Intance.Reslove<IAuthenticationService>();}[HttpGet]public void AccountsAPI(){}/// <summary>/// 登录API/// </summary>/// <param name="loginIdorEmail">登录帐号(邮箱或者其他LoginID)</param>/// <param name="hashedPassword">加密后的密码,这里避免明文,客户端加密后传到API端</param>/// <param name="deviceType">客户端的设备类型</param>/// <param name="clientId">客户端识别号, 一般在APP上会有一个客户端识别号</param>/// <remarks>其他的登录位置啥的,是要客户端能传的东西,都可以在这里扩展进来</remarks>/// <returns></returns>[Route("account/login")]public SessionObject Login(string loginIdorEmail, string hashedPassword, int deviceType = 0, string clientId = ""){if (string.IsNullOrEmpty(loginIdorEmail))throw new ApiException("username can't be empty.", "RequireParameter_username");if (string.IsNullOrEmpty(hashedPassword))throw new ApiException("hashedPassword can't be empty.", "RequireParameter_hashedPassword");int timeout = 60;var nowUser = _authenticationService.GetUserByLoginId(loginIdorEmail);if (nowUser == null)throw new ApiException("Account Not Exists", "Account_NotExits");#region Verify Passwordif (!string.Equals(nowUser.Password, hashedPassword)){throw new ApiException("Wrong Password", "Account_WrongPassword");}#endregionif (!nowUser.IsActive)throw new ApiException("The user is inactive.", "InactiveUser");UserDevice existsDevice = _authenticationService.GetUserDevice(nowUser.UserId, deviceType);// Session.QueryOver<UserDevice>().Where(x => x.AccountId == nowAccount.Id && x.DeviceType == deviceType).SingleOrDefault();if (existsDevice == null){string passkey = MD5CryptoProvider.GetMD5Hash(nowUser.UserId + nowUser.LoginName + DateTime.UtcNow.ToString() + Guid.NewGuid().ToString());existsDevice = new UserDevice(){UserId = nowUser.UserId,CreateTime = DateTime.UtcNow,ActiveTime = DateTime.UtcNow,ExpiredTime = DateTime.UtcNow.AddMinutes(timeout),DeviceType = deviceType,SessionKey = passkey};_authenticationService.AddUserDevice(existsDevice);}else{existsDevice.ActiveTime = DateTime.UtcNow;existsDevice.ExpiredTime = DateTime.UtcNow.AddMinutes(timeout);_authenticationService.UpdateUserDevice(existsDevice);}nowUser.Password = "";return new SessionObject() { SessionKey = existsDevice.SessionKey, LogonUser = nowUser };}}
复制代码

API身份验证

身份信息的认证是通过Web API 的 ActionFilter来实现的, 每各需要身份验证的API请求都会要求客户端传一个SessionKey在URL里面丢过来。

在这里我们通过一个自定义的SessionValidateAttribute来做客户端的身份验证, 其继承自 System.Web.Http.Filters.ActionFilterAttribute, 把这个Attribute加在每个需要做身份验证的ApiControler上面,这样该 Controller下面的所有Action都将拥有身份验证的功能, 这里会存在如果有少量的API不需要身份验证,那该如何处理,这个会做一些排除,为了保持文章的思路清晰,这会在后续的章节再说明.

复制代码
public class SessionValidateAttribute : System.Web.Http.Filters.ActionFilterAttribute{public const string SessionKeyName = "SessionKey";public const string LogonUserName = "LogonUser";public override void OnActionExecuting(HttpActionContext filterContext){var qs = HttpUtility.ParseQueryString(filterContext.Request.RequestUri.Query);string sessionKey = qs[SessionKeyName];if (string.IsNullOrEmpty(sessionKey)){throw new ApiException("Invalid Session.", "InvalidSession");}IAuthenticationService authenticationService = IocManager.Intance.Reslove<IAuthenticationService>();//validate user sessionvar userSession = authenticationService.GetUserDevice(sessionKey);if (userSession == null){throw new ApiException("sessionKey not found", "RequireParameter_sessionKey");}else{//todo: 加Session是否过期的判断if (userSession.ExpiredTime < DateTime.UtcNow)throw new ApiException("session expired", "SessionTimeOut");var logonUser = authenticationService.GetUser(userSession.UserId);if (logonUser == null){throw new ApiException("User not found", "Invalid_User");}else{filterContext.ControllerContext.RouteData.Values[LogonUserName] = logonUser;SetPrincipal(new UserPrincipal<int>(logonUser));}userSession.ActiveTime = DateTime.UtcNow;userSession.ExpiredTime = DateTime.UtcNow.AddMinutes(60);authenticationService.UpdateUserDevice(userSession);}}private void SetPrincipal(IPrincipal principal){Thread.CurrentPrincipal = principal;if (HttpContext.Current != null){HttpContext.Current.User = principal;}}}
复制代码

OnActionExcuting方法:

这个是在进入某个Action之前做检查, 这个时候我们刚好可以同RequestQueryString中拿出SessionKey到UserDevice表中去做查询,来验证Sessionkey的真伪, 以达到身份验证的目的。

用户的过期时间:

在每个API访问的时候,会自动更新Session(也就是UserDevice)的过期时间, 以保证SessionKey不会过期,如果长时间未更新,则下次访问会过期,需要重新登录做处理。

Request.IsAuthented:

上面代码的最后一段SetPrincipal就是来设置我们线程上下文及HttpContext上下文中的用户身份信息, 在这里我们实现了我们自己的用户身份类型

复制代码
public class UserIdentity<TKey> : IIdentity{public UserIdentity(IUser<TKey> user){if (user != null){IsAuthenticated = true;UserId = user.UserId;Name = user.LoginName.ToString();DisplayName = user.DisplayName;}}public string AuthenticationType{get { return "CustomAuthentication"; }}public TKey UserId { get; private set; }public bool IsAuthenticated { get; private set; }public string Name { get; private set; }public string DisplayName { get; private set; }}public class UserPrincipal<TKey> : IPrincipal{public UserPrincipal(UserIdentity<TKey> identity){Identity = identity;}public UserPrincipal(IUser<TKey> user): this(new UserIdentity<TKey>(user)){}/// <summary>/// /// </summary>public UserIdentity<TKey> Identity { get; private set; }IIdentity IPrincipal.Identity{get { return Identity; }}bool IPrincipal.IsInRole(string role){throw new NotImplementedException();}}public interface IUser<T>{T UserId { get; set; }string LoginName { get; set; }string DisplayName { get; set; }}
复制代码

这样可以保证我们在系统的任何地方,通过HttpContext.User 或者 System.Threading.Thread.CurrentPrincipal可以拿到当前线程上下文的用户信息, 方便各处使用

加入身份认证之后的Product相关API如下:

复制代码
[RoutePrefix("api/products"), SessionValidate]public class ProductController : ApiController{[HttpGet]public void ProductsAPI(){ }/// <summary>/// 产品分页数据获取/// </summary>/// <returns></returns>[HttpGet, Route("product/getList")]public Page<Product> GetProductList(string sessionKey){return new Page<Product>();}/// <summary>/// 获取单个产品/// </summary>/// <param name="productId"></param>/// <returns></returns>[HttpGet, Route("product/get")]public Product GetProduct(string sessionKey, Guid productId){return new Product() { ProductId = productId };}/// <summary>/// 添加产品/// </summary>/// <param name="product"></param>/// <returns></returns>[HttpPost, Route("product/add")]public Guid AddProduct(string sessionKey, Product product){return Guid.NewGuid();}/// <summary>/// 更新产品/// </summary>/// <param name="productId"></param>/// <param name="product"></param>[HttpPost, Route("product/update")]public void UpdateProduct(string sessionKey, Guid productId, Product product){}/// <summary>/// 删除产品/// </summary>/// <param name="productId"></param>[HttpDelete, Route("product/delete")]public void DeleteProduct(string sessionKey, Guid productId){}
复制代码

可以看到我们的ProductController上面加了SessionValidateAttribute, 每个Action参数的第一个位置,加了一个string sessionKey的占位, 这个主要是为了让Swagger.Net能在UI上生成测试窗口

image

这篇并没有使用OAuth等授权机制,只是简单的实现了登录授权,这种方式适合小项目使用.

这里也只是实现了系统的登录,API访问安全,并不能保证 API系统的绝对安全,我们可以透过 路由的上的HTTP消息拦截, 拦截到我们的API请求,截获密码等登录信息, 因此我们还需要给我们的API增加SSL证书,实现 HTTPS加密传输。

另外在前几天的有看到结合客户端IP地址等后混合生成 Sessionkey来做安全的,但是也具有一定的局限性, 那种方案合适,还是要根据自己的实际项目情况来确定.

由于时间原因, 本篇只是从原理方面介绍了API用户登录与访问身份认证,因为这部分真实的测试设计到数据库交互, Ioc等基础设施的支撑,所以这篇的代码只能出现在SwaggerUI中,但是无法实际测试接口。在接下来的代码中我会完善这部分.

代码: 代码下载(代码托管在CSDN Code)

相关文章:

ASP.NET中的页面指示标识

页面指示标识 的功能是用来确定在处理aspx文件的时候&#xff0c;需要系统做一些什么特殊的设定&#xff1f;它的语法是&#xff1a;<% directive attributevalue %> 比如&#xff1a;<%import namespace"System.Data"%> 注意属性之间需要空格&#…

⑨③-不能浪费拥有的年轻资本

⑨③-不能浪费拥有的年轻资本 老男孩语录⑨③-不能浪费拥有年轻资本 万两黄金易得&#xff0c;年轻资本难求&#xff01; 同学们&#xff0c;我们不能浪费比万两黄金还贵重的年轻资本&#xff0c;因为&#xff0c;转眼间你就不再拥有这个资本了&#xff0c; 趁着年轻&#xff0…

滴滴AI语音团队获国际顶尖智能对话系统竞赛世界第一

出品 | AI科技大本营头图 | CSDN付费下载于视觉中国近日&#xff0c;在国际顶尖人工智能竞赛第九届对话系统技术挑战赛&#xff08;DSTC9&#xff09;中&#xff0c;滴滴语音和NLP技术团队参与端到端多领域面向任务型对话系统任务荣获世界第一&#xff0c;充分彰显滴滴公司在自…

RHEL5+PXE+DHCP+Apache+Kickstart安装RHCE5.1 i386实验室环境

此博文出自&#xff1a;http://andrewyu.blog.51cto.com/1604432/1100567 使用RHEL5PXEDHCPApacheKickstart安装RHCE5.1 i386实验室环境&#xff0c;根据文章内容也可批量安装了红帽RHEL系统或CentOS5.x&#xff0c;现阶段&#xff0c;由于需要大规模应用CentOS5.8 x86_64系统用…

用ASP.Net(C#)连接Oracle数据库的方法

今天看了一下asp.net连接oracle数据库的方法&#xff0c;得到了如下代码。这段代码打开了MyTable表&#xff0c;并把操作员的名字列出。字段类型是OracleString。读取的时候用的是字段编号&#xff0c;我不知道怎么使用字段名来读取某字段的内容。下面是代码&#xff1a;//首先…

NLP任务中的文本预处理步骤、工具和示例

作者 | Viet Hoang Tran Duong来源 | DeepHub IMBA头图 | CSDN付费下载于视觉中国数据是新的石油&#xff0c;文本是我们需要更深入钻探的油井。文本数据无处不在&#xff0c;在实际使用之前&#xff0c;我们必须对其进行预处理&#xff0c;以使其适合我们的需求。对于数据也是…

github设置添加SSH

https 和 SSH 的区别&#xff1a; 1、前者可以随意克隆github上的项目&#xff0c;而不管是谁的&#xff1b;而后者则是你必须是你要克隆的项目的拥有者或管理员&#xff0c;且需要先添加 SSH key &#xff0c;否则无法克隆。 2、https url 在push的时候是需要验证用户名和密码…

在asp.net中使用客户端脚本

我们常常在asp.net中需要使用到客户端脚本&#xff0c;在asp.net中使用客户端脚本很容易&#xff0c;使用到Page的RegisterClientScriptBlock方法就行了。下面是我写的一段C#代码,用来打开新窗口&#xff1a; using System; using System.Web.UI; namespace MyBill { …

一个DIV调用多个CSS样式

1使用以上两个CSS样式表对描述同一个DIV的写法class利用class可以对于同一个标签多重定义样式. 比如用1、2两种样式同时控制一个DIV&#xff0c;可以写成以下格式&#xff1a; <div class"1 2"> </div> 使用以上两个CSS样式表对描述同一个DIV&#xff0…

最新!百度首发 OCR 自训练平台 EasyDL OCR

今年以来&#xff0c;人工智能愈发火热。在2020年4月&#xff0c;政府已将人工智能基础设施列入新基建范围。在利好政策引导下&#xff0c;人工智能的应用范围越来越广。以 OCR&#xff08;文字识别技术&#xff09;为例&#xff0c;随着智能手机与各种端边电子产品的增多&…

性能测试初学_loadrunner base64/md5 编码 解码

参考这3篇文章&#xff1a; MD5&#xff1a; http://bbs.51testing.com/forum.php?modviewthread&tid1111323 base64&#xff1a; http://www.cnblogs.com/preftest/archive/2011/06/12/2079178.html http://www.51testing.com/html/41/15103841-3707341.html 主要思路为&…

ASP.NET管理状态的十种途径

HTTP协议是无状态的&#xff0c;ASP.NET提供了丰富的手段在页面之间管理状态。本文列举ASP.NET管理状态的十种途径。 ASP.NET中&#xff0c;从System.Web.UI.Page继承的类里有以下十种管理页面状态的途径&#xff1a; 1. Application对象: this.Application 2. …

sed学习系列---第3/3部分

为什么80%的码农都做不了架构师&#xff1f;>>> ---简介 在这篇 sed 系列的总结性文章中&#xff0c;Daniel Robbins 带您体验 sed 的真正力量。在介绍完几个重要的 sed 脚本之后&#xff0c;他将通过将一个 Quicken .QIF 文件转换成可读文本格式来演示一些基本 s…

11位科幻作家参与,首次AI人机共创写作实验启动

人工智能会怎样影响人类文学创作&#xff1f;人类智慧与机器智慧如何相互激发创作灵感&#xff1f; 10月27日&#xff0c;由传茂文化和创新工场联手打造的华语科幻AI人机共创写作实验项目《共生纪》启动&#xff0c;人类作家与AI算法将围绕环保、人机关系、性别、文化多样性等…

笔记之远程桌面服务(RDS)

Windows默认只能有2个用户同时通过RDP进行连接&#xff0c;非常不方便&#xff0c;于是借此机会学习了下Win2012R2的远程桌面配置。以下我把学习过程记录一下&#xff1a; 1. 最开始我觉得只需要安装“Remote Desktop Session Host”&#xff0c;事实证明这样没错&#xff0c;可…

用Asp.net 传送大文件

Chris Hynes我们在上传大文件时都遇到过这样或那样的问题。设置很大的maxRequestLength值并不能完全解决问题&#xff0c;因为ASP.NET会block直到把整个文件载入内存后&#xff0c;再加以处理。实际上&#xff0c;如果文件很大的话&#xff0c;我们经常会见到Internet Explorer…

[转]Java Os Properties

2019独角兽企业重金招聘Python工程师标准>>> // File: io/properties/SysPropList.java // Description: Shows system properties. This must be an application. // An applet cant get this information. // Author: Fred Swartz // Date: 2 Feb…

思谋科技A轮融资超1亿美元 ,成为最年轻“准独角兽”AI企业

新一代视觉AI前沿技术公司——思谋科技今天宣布&#xff0c;已完成A轮融资。据知情人士透露&#xff0c;思谋科技此次融资金额超1亿美元&#xff0c;成为业内最年轻的“准独角兽”AI企业&#xff0c;本新投资方包括松禾资本、红杉资本中国基金、基石资本、闻天下投资等。今年6月…

C语言 · 征税程序

算法提高 征税程序 时间限制&#xff1a;1.0s 内存限制&#xff1a;512.0MB问题描述税务局希望你帮他们编写一个征税程序&#xff0c;该程序的功能是&#xff1a;首先输入某公司的年销售额sale和税率rate&#xff0c;然后程序将计算出相应的税额tax&#xff0c;并把它显示在…

在ASP.NET中随意创建图形信息

如果没有一个外部组件的支持&#xff0c;在ASP中是不能动态创建图形的&#xff0c;不管它是一个图表&#xff0c;一个横幅或仅仅是一个图形计数器。可喜的是&#xff0c;这一点在ASP.NET中改变了。现在&#xff0c;我们只需要使用内置功能&#xff0c;就能够很容易动态创建图形…

做动态图表没有数据?用Python就能获取

来源 | 法纳斯特&#xff08;ID:walker398&#xff09;刷爆全网的动态条形图&#xff0c;原来 5 行 Python 代码就能实现&#xff01;这是小F在国庆之前写的一篇文章&#xff0c;既然有了Python这个制作动态条形图工具&#xff0c;缺的那便是数据了。先看一下B站2019年「数据可…

CCTouchDispatcher sharedDispatcher 方法过期

//[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES]; [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:selfpriority:0swallowsTouches:YES];转载于:https://www.cnblogs.com/sell/archive/2013/01/14/2…

哪些听起来像段子一样的故事?

杭州海底世界&#xff0c;一个小走廊两边都是各种爬行动物展览。有两只蜥蜴当时是这个样子人还年轻&#xff0c;还比较猥琐&#xff0c;看到一个趴在另一个身上就觉得在做什么羞羞的事。于是就拍下来&#xff0c;发到群里&#xff0c;然后说了句交配中。然后一天就光拍照&#…

立足高校矢志创新,鲲鹏计算生态长沙在行动

在今年&#xff0c;虽然全球经济发展受到疫情和国际形势变化影响增速减缓&#xff0c;但我国在AI、云计算、大数据、5G等前沿技术方面的发展却依然没有停下脚步。发展前沿技术&#xff0c;计算产业是基础。因此&#xff0c;培养计算产业人才成为当前全社会共同面对的一大关键。…

用好ASP.NET 2.0的URL映射

简介&#xff1a;   URL映射是ASP.NET 2.0中提供的新特性。URL映射技术帮助我们将一个特定URL映射为另一个URL。为了帮助理解&#xff0c;我们假设你在站点有一个叫Homepage.aspx的页面来访问主页&#xff0c;所有的用户也都用这个页面来访问你的主页。但由于某些原因&#x…

N皇后问题的位运算求解——目前最快的方法

核心代码如下&#xff1a; 1 void test(int row, int ld, int rd) 2 { 3 int pos, p; 4 if ( row ! upperlim ) 5 { 6 pos upperlim & (~(row | ld | rd )); 7 while ( pos ) 8 { 9 p pos & (~pos 1); 1…

cocos2d-x JS 获取当前系统时间(解决屏幕双击点击事件)

记录一下&#xff0c;好开心&#xff0c;感觉今天自己又学到东西了&#xff0c;对于屏幕双击事件本来还毫无头绪的&#xff0c;今天得以解决总算没白费加班&#xff0c;其实原理很简单&#xff1b;就是在点击事件里做一个判断&#xff0c;这个判断就是需要获取当前系统的时间的…

怎么样给下拉框加载背景色

选择自 PPLUNCLE 的 Blog 部分代码:------aspx页面&#xff1a; <tr> <td><select id“job“ name“job“ runat“server“></td></tr>说明&#xff1a;下拉框必须写成上面的形式&#xff0c;用<asp:dropdownlist....>不能实现------cs页…

Windows 7 部署(一):安装和部署简述

术语表&#xff1a; 英文缩写英文全称中文全称备注 MDT Microsoft Deployment Toolkit 微软部署工具包 免费&#xff0c;需要域环境及DHCP服务器(Port 66 and 67)&#xff0c;需要首先安装WAIK。使用MDT部署Windows 7时&#xff0c;也需要WDS&#xff0c;WDS主要负责推送WinP…

希捷发布CORTX对象存储软件与开源社区,普惠超大规模数据存储

数据存储和管理解决方案提供商希捷科技今日&#xff08;10月29日&#xff09;举行了“Let’s Rethink Data”希捷科技Datasphere线上峰会。在本次峰会&#xff0c;希捷科技携手行业专家与生态合作伙伴洞见数据产业趋势&#xff0c;并发布了对象存储软件CORTXTM以及CORTXTM开源社…