【WEB API項目實戰干貨系列】- 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中,但是無法實際測試接口。在接下來的代碼中我會完善這部分.


文章轉載出自 :http://www.cnblogs.com/Flyear/p/4875066.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/286493.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/286493.shtml
英文地址,請注明出處:http://en.pswp.cn/news/286493.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql數據庫建立的數據庫在哪個文件夾?

為什么80%的碼農都做不了架構師&#xff1f;>>> 一般在安裝目錄下的data文件夾下&#xff0c;或者在C:\Documents and Settings\All Users\Application Data\MySQL\MySQL Server 5.1\data&#xff08;你的可能是C:\Documents and Settings\All Users\Application D…

python 學習筆記01

python學習過程遇到一些問題記錄&#xff1a; 1、 IndentationError:expected an indented block錯誤的解決辦法 一句話 有冒號的下一行往往要縮進&#xff0c;該縮進就縮進 參考資料&#xff1a;http://blog.csdn.net/hongkangwl/article/details/16344749 2、17個新手常見Pyt…

ArcGIS實驗教程——實驗二十四:人口密度制圖

ArcGIS實驗視頻教程合集:《ArcGIS實驗教程從入門到精通》(附配套實驗數據)》 一、實驗分析 人口密度是指單位土地面積上居住的人口數,通常以每平方千米或每公頃內的常住人口為單位計算。人口密度同資源、經濟密切結合,因此,科學準確地分析人口密度的分布情況,對合理制定…

Navicat 遠程連接ubuntu出現的問題

2003-Cantt connect to Mysql server to xxxxxxx 解決&#xff1a; vim /etc/mysql/my.cnf 修改bind-address 0.0.0.0 然后重啟mysql&#xff1a; 這時進入mysql可能會報錯&#xff1a; ERROR 2002 (HY000): Cant connect to local MySQL server through socket /v…

WPF效果第一百八十八篇之再玩Expander

大端午節的在屋里吹著空調擼著代碼真是酸爽;閑話也不多扯,直接看今天要分享的效果:1、關于簡單的布局設計:2、前臺先來個死布局,回頭ListBox改模板:<Expander ExpandDirection"Left" Header"控制卡" VerticalAlignment"Bottom" HorizontalAli…

Android之實現長按Webview頁面文字自定義復制、全選、分享、搜索、翻譯功能(支持多語言,博文也有Demo下載地址)

1 需求和效果爆照 瀏覽器app封裝了Webview,然后實現實現長按Webview頁面文字自定義復制、全選、分享、搜索、翻譯功能(支持多語言),都在自己的瀏覽器app里面進行搜索和翻譯,不跳到系統瀏覽器里面去 效果爆照如下,oppo手機效果如下 華為手機效果如下 2 Demo下載地址 De…

中國西北地區專題地圖合集(高清)

1. 西北地區概況圖 2. 西北地區植被類型分布圖 3. NDVI變化趨勢圖 4. 氣候與NDVI的相關性

Apache、tomcat、Nginx常用配置合集

配置文件地址&#xff1a; Apache&#xff1a; /etc/httpd/conf/httpd.conf tomcat&#xff1a; /usr/local/tomcat/conf/server.xml Nginx &#xff1a; /usr/local/nginx/conf/nginx.conf 開機啟動文件&#xff1a;/etc/rc.d/rc.local 啟動方式&#xff1a; Apache&#xff…

使用putty連接linux

使用putty連接linux 快照的使用 &#xff0c;做快照相當于做備份&#xff0c;比如配置好IP&#xff0c;快照一下&#xff0c;下次就可以在回到這里&#xff01; putty下載 最好去官網下載 下載putty.zip如圖所示 如何使用putty 如圖設置好IP然后 save 保存 如…

【WEB API項目實戰干貨系列】- API訪問客戶端(WebApiClient適用于MVC/WebForms/WinForm)(四)

目前最新的代碼已經通過Sqlite NHibernate Autofac滿足了我們基本的Demo需求. 按照既定的要求&#xff0c;我們的API會提供給眾多的客戶端使用, 這些客戶端可以是各種Web站點, APP, 或者是WinForm, WPF, Silverlight等諸如此類的應用&#xff0c;將來還有可能是各種Iot等物聯…

基于 Roslyn 實現代碼動態編譯

基于 Roslyn 實現代碼動態編譯Intro之前做的一個數據庫小工具可以支持根據 Model 代碼文件生成創建表的 sql 語句&#xff0c;原來是基于 CodeDom 實現的&#xff0c;后來改成使用基于 Roslyn 去做了。實現的原理在于編譯選擇的Model 文件生成一個程序集&#xff0c;再從這個程…

【GIS風暴】GIS拓撲關系原理詳解

目 錄 1. 拓撲關系的概念2. 拓撲元素3. 拓撲關系4. 拓撲關系的意義5. 拓撲在ArcGIS中實現1. 拓撲關系的概念 地圖上的拓撲關系是指圖形在保持連續狀態下的變形(縮放、旋轉和拉伸等),但圖形關系不變的性質。 2. 拓撲元素 對二維而言,矢量數據可抽象為點(節點)、線(鏈、…

Android之簡單的文件夾選擇器實現

1、效果爆照 2、代碼實現 前提需要保證app有讀寫權限 activity_select_folder.xml文件如下 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layo…

【iVX 初級工程師培訓教程 10篇文拿證】04 畫布及我和 iVX 合照

目錄 【iVX 初級工程師培訓教程 10篇文拿證】01 了解 iVX 完成新年賀卡 【iVX 初級工程師培訓教程 10篇文拿證】02 數值綁定及自適應網站制作 【iVX 初級工程師培訓教程 10篇文拿證】03 事件及猜數字小游戲 【iVX 初級工程師培訓教程 10篇文拿證】04 畫布及我和 iVX 合照 【iV…

360極速瀏覽器使用postman

步驟如下&#xff1a;1、將crx文件打包成zip文件2、解壓打包的zip文件&#xff0c;并將_metadata文件夾修改為metadata3、打開360瀏覽器的擴展4、360瀏覽器加載postman插件5、創建快捷方式6、雙擊快捷方式打開postman下載地址&#xff1a;http://pan.baidu.com/s/1c1ZX8XE如果網…

centos 下安裝man手冊

安裝centos minimal版本&#xff0c;發現沒有man手冊 需要安裝一下&#xff0c;yum install man-pages 本文轉自 XDATAPLUS 51CTO博客&#xff0c;原文鏈接:http://blog.51cto.com/xdataplus/1796126

# javascript 總結

# javascript 總結 ## 語法1. 區分大小寫2. 命名規范1. 首字母必須是 字母 _ $2. 其他字符可以是 數字 字母 下劃線 $3. 避開系統的關鍵字4. 單詞和單詞連接方式推薦駝峰命名3. 注釋1. 單行注釋 //注釋的內容2. 多行注釋 /*注釋內容*/4. 語句1. 要用;結尾(推薦做法)2. 如果不寫…

聊聊 C++ 和 C# 中的 lambda 玩法

這幾天在看 C 的 lambda 表達式&#xff0c;挺有意思&#xff0c;這個標準是在 C11標準 加進去的&#xff0c;也就是 2011 年&#xff0c;相比 C# 2007 還晚了個 4 年&#xff0c; Lambda 這東西非常好用&#xff0c;會上癮&#xff0c;今天我們簡單聊一聊。一&#xff1a;語法…

Android之網絡請求通過協程+okhttp的沒有做網絡異常處理導致程序奔潰問題

1 問題 app里面的網絡請求是通過協程+okhttp來實現的,但是沒有做網絡異常處理(域名無法解析、502錯誤等等一系列),導致程序奔潰 2 嘗試 因為app基本上做好了,外面有大幾十個地方調用,然后又有不同的作用域,調用的地方太多了,一開始修改在最外出的網絡請求地方直接加上…

Windows10系統重裝后必不可少的優化步驟

1. 查看系統的激活狀態 Win+R,打開運行,輸入slmgr.vbs -xpr,回車! 可以看到,該系統沒有永久激活,即將過期,過期后部分功能會不可使用,需要重新激活。 2. 徹底關掉Windows Defender 方法一: 打開“命令提示符(管理員)”,然后輸入: reg add "HKEY_LOCAL_MA…