在我的混合式開發框架里面,集成了WebAPI的訪問,這種訪問方式不僅可以實現簡便的數據交換,而且可以在多種平台上進行接入,如Winform程序、Web網站、移動端APP等多種接入方式,Web API的處理方式和微信提供的接口處理規則類似,也是通過向服務器獲得訪問令牌(AccessToken),然后傳遞給每個Web API接口,實現數據的交換處理。本篇隨筆主要介紹混合框架中Winform對Web API訪問的處理。
1、Web API接入方式介紹
《混合式開發框架》混合了Web API接口訪問、WCF接口訪問,以及直接訪問數據庫三種方式的接入,以適應多種場景的應用,是基於門面層的一種接口實現處理和封裝。是一種彈性化非常好的框架應用,既可用於單機版軟件或者基於局域網內的應用軟件,也可以用於分布式技術的互聯網環境應用,是一種成熟穩定、安全高效的技術框架。
關於這個框架的詳細介紹,可以查看我的隨筆《Winform混合式開發框架的特點總結》進行詳細了解。
這里主要關注Web API的接入方式,我們知道,如果是一般的接口,如果公布在互聯網上面,就會有很多接入的風險,因此需要對接口的調用進行檢查校驗,確保訪問令牌有效,而且對數據發生修改的,還需要對數據的加密簽名進行檢查,才能保證我們的接口運行在較為安全的環境中。
混合框架調用Web API接口的詳細過程,可以通過《Web API應用架構在Winform混合框架中的應用(3)--Winfrom界面調用WebAPI的過程分解》、《Web API應用架構在Winform混合框架中的應用(1)》、《Web API接口設計經驗總結》進行了解。
2、Web API的接口訪問令牌的處理
由於我們需要對接口訪問的身份進行核實,因此一般要求我們的接口都帶有一個token參數,用來對用戶身份進行識別,如下所示是Web API層的MVC控制器的接口定義。
[HttpGet] public UserInfo GetUserByName(string userName, string token) { //令牌檢查,不通過則拋出異常 CheckResult checkResult = CheckToken(token); return BLLFactory<User>.Instance.GetUserByName(userName); }
如果我們在客戶端需要調用這個接口,那么就需要傳入這個token參數,也就是說這個token令牌需要在調用任何接口前獲得,這樣才能為我們后面的接口調用做好准備。
而這個token的產生是非常重要的,需要嚴格頒發,因此需要對獲取這個token的方法的參數進行簽名校驗,如下面代碼是WebAPI接口對產生token的處理。
/// <summary> /// 注冊用戶獲取訪問令牌接口 /// </summary> /// <param name="username">用戶登錄名稱</param> /// <param name="password">用戶密碼</param> /// <param name="signature">加密簽名字符串</param> /// <param name="timestamp">時間戳</param> /// <param name="nonce">隨機數</param> /// <param name="appid">應用接入ID</param> [HttpGet] public TokenResult GetAccessToken(string username, string password, string signature, string timestamp, string nonce, string appid)
也就是需要傳入用戶名、密碼、加密簽名、時間戳、隨機數、應用接入ID等信息,從而構建出來一個訪問令牌,通過用戶名、密碼、加密簽名校驗等方式,可以實現對訪問令牌(token)的嚴格頒發處理。
在客戶端調用所有Web API接口前,我們需要先通過上面的Web API接口,獲取到該用戶的訪問令牌,為了方便,我們可以在客戶端封裝一個函數,通過這個函數獲取到對應的訪問令牌,然后把它存儲在緩存里面,方便各個模塊的接口訪問處理。
/// <summary> /// 用戶獲取令牌的輔助類 /// </summary> public class AccessTokenHelper { private const string APPID = "APPID";//應用ID,由系統管理員分配 private const string APPSECRET = "APPSECRET";//應用秘鑰,,由系統管理員分配 private const string DEFAULT_API_URL = "http://localhost:9001/api/Auth/GetAccessToken";//默認調試的Web API獲取授權地址 /// <summary> /// 設置簽名參數。 /// 由於Web API大多數的接口,都需要驗證用戶身份的訪問令牌(accesstoken),因此用戶在登陸的時候,需要使用這個步驟去獲取令牌信息,然后在繼續后續的接口操作。 /// 該接口用到的應用ID、應用秘鑰等參數,由系統管理員統一分配。 /// </summary> public static bool GetAccessToken(string username, string password) { bool result = false; //配置使用Web API模式,需要構建登陸token才能訪問 AppConfig config = new AppConfig(); string callerType = config.AppConfigGet("CallerType"); string apiUrl = config.AppConfigGet("AuthApiUrl"); apiUrl = string.IsNullOrEmpty(apiUrl) ? DEFAULT_API_URL : apiUrl; if (callerType.Equals("api", StringComparison.OrdinalIgnoreCase)) { //使用API方式,需要在緩存里面設置特殊的信息 var url = apiUrl + SignatureHelper.GetSignatureUrl(APPID, APPSECRET); url += string.Format("&username={0}&password={1}", username, password); TokenResult tokenResult = JsonHelper<TokenResult>.ConvertJson(url); result = !string.IsNullOrEmpty(tokenResult.access_token); if (tokenResult == null) { var message = "獲取授權信息出錯,請檢查地址是否正確!"; MessageDxUtil.ShowError(message); } var SignatureInfo = new SignatureInfo() { appid = APPID, appsecret = APPSECRET, token = (tokenResult != null) ? tokenResult.access_token : null }; Cache.Instance.Add("SignatureInfo", SignatureInfo); } return result; }
有了這個輔助方法,我們可以在程序啟動后,用戶進行身份登錄的時候,先調用這個方法來獲取令牌。
string ip = NetworkUtil.GetLocalIP(); string macAddr = HardwareInfoHelper.GetMacAddress(); string loginName = this.txtLoginName.Text.Trim(); string password = this.txtPassword.Text; //如果無法獲取訪問令牌,則返回 bool hasGotToken = AccessTokenHelper.GetAccessToken(loginName, password); if (!hasGotToken) { return; }
剛才我提到了Web API層的MVC控制器的接口定義,默認后面一般都有一個token參數,如下代碼所示
[HttpGet] public UserInfo GetUserByName(string userName, string token) { //令牌檢查,不通過則拋出異常 CheckResult checkResult = CheckToken(token); return BLLFactory<User>.Instance.GetUserByName(userName); }
而我們為了方便客戶端調用,一般在客戶端調用Web API的時候進行簡化了一下,把token參數拿掉,它的值從緩存里面提取。如客戶端調用的封裝代碼如下所示。
public UserInfo GetUserByName(string userName) { var action = "GetUserByName"; string url = GetTokenUrl(action) + string.Format("&userName={0}", userName); UserInfo result = JsonHelper<UserInfo>.ConvertJson(url); return result; }
其中GetTokenUrl就是我們根據token和方法名稱,構建一個連接字符串,函數實現如下所示。
/// <summary> /// 獲取單純包含token參數的連接 /// </summary> /// <param name="action">控制器方法名稱</param> /// <returns></returns> protected string GetTokenUrl(string action) { string url = ""; if (this.SignatureInfo != null) { var append = string.Format("?token={0}", SignatureInfo.token); string baseUrl = GetBaseUrl(); url = CombindUrl(baseUrl, action + append);//組合為完整的訪問地址 } else { throw new ArgumentNullException("沒有在緩存里面設置SignatureInfo簽名信息"); } return url; }
這樣最終我們可以獲得類似下面的連接地址:
http://localhost:27206/api/Account/GetAccountTypeList?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIxIiwiaWF0IjoxNDYzNTU3OTAzLCJqdGkiOiI3OGMyOGRhNC01ZjRjLTQxYzItOThkNC1lYmFkZTM3YjA4NjUiLCJuYW1lIjoiYWRtaW4iLCJjaGFubmVsIjoiMCIsInNoYXJlZGtleSI6IjEyMzRhYmNkIn0.DysdbGx70xuIxXBz3G3x3MkGh9ZxL2zF9Fzu8FGVS0w
有了這個令牌組裝好的URL,我們可以對訪問結果的JSON字符串進行解析,把它解析為對應的數據就可以了。
當然,在實際的Web API接口開發過程中,我們還可以使用Web API工具進行接口調試,如下所示。
下面的1-5的標識就是獲取token所需要的簽名數據,當然連接還帶有幾個賬號認證所需要的信息了,如賬號密碼、所在公司等信息。
當然我們也可以使用瀏覽器進行測試獲取Token的信息,只是沒有那么方便而已。