1. 程式人生 > >ASP.NET WebAPI 雙向token實現對接小程式登入邏輯

ASP.NET WebAPI 雙向token實現對接小程式登入邏輯

最近在學習用asp.net webapi搭建小程式的後臺服務,因為基於小程式端和後臺二者的通訊,不像OAuth(開放授權),存在第三方應用。所以這個token是雙向的,一個是對使用者的,一個是對介面的。本來做了一份是用Oauth的,用的是第三種密碼策略模式。但是因為不存在第三方應用,所以不用Oauth這種授權標準。 這個Sample是用簡單三層做的,書上得來終覺淺,絕知此事要躬行,實踐一次就知道wepapi與前端如何通過token認證進行邏輯互動。

搭建專案

  1. 搭建專案這一點不多說,直接新建一個空的,

    為什麼會用MVC裡用到的AuthorizationFilter呢,具體其實我當時不知道WebAPI裡面能不能用,但因為領導說最好小程式訪問進來,有一個統一驗證的方法。因為我之前在另一個專案裡建立了一個控制元件器基類,BaseController,用於做登入驗證,許可權驗證,日誌記錄,以及公共方法。因為用了OnActionExecuting,所以當時想也沒有想,直接搜尋Web API OnActionExecuting,看到Web API也有Filter的相關資料。最後參考了[Web APi之認證(Authentication)]((

    https://www.cnblogs.com/CreateMyself/p/4857799.html),從而順利完成了這個雙向token認證邏輯。 首先,在Controllers資料夾裡建立AuthFilterAttribute,即自定義Filter特性。這個class裡面先重寫OnAuthorization方法。

    ///

    /// 最先執行的Filter,被用作請求許可權校驗 /// public class AuthFilterAttribute : AuthorizationFilterAttribute { public override void OnAuthorization(HttpActionContext actionContext) {} }

token規則以及解析請求報文頭

   OnAuthorization是重寫的。那麼具體應該寫什麼呢?當然是進行驗證當前的請求是否有授權,是否是 符合要求的請求報文頭。
   public override void OnAuthorization(HttpActionContext actionContext)
    {
        //如果使用者方位的Action帶有AllowAnonymousAttribute,則不進行授權驗證
        if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
        {
            return;
        }
        string authParameter = null;
        var authValue = actionContext.Request.Headers.Authorization;//actionContext:Action方法請求上下文
        if (authValue != null && authValue.Scheme == "BasicAuth")//這裡有BasicAuth和參考資產裡面的不同,我們沒有認定類,這裡的BasicAuth就算是我們自定義的token規則。其實主要是我還沒有了解認證身份以及瞭解GenericIdentity。
        {
            authParameter = authValue.Parameter;  //獲取請求引數
            var authToken = authParameter.Split('|');  //取出引數,引數格式為(當前時間:加密後的token)將其進行分割
            Logging.Error(authParameter);
            if (authToken.Length < 2)
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.NotAcceptable);//引數不完整,返回406不接受
            }
            else
            {
                
                //引數完整,進行驗證
                if (ValidateToken(authToken[0],authToken[1]))
                {
                    base.OnAuthorization(actionContext);
                }
                else
                {
                    actionContext.Response = new HttpResponseMessage(HttpStatusCode.ExpectationFailed);//驗證不通過,未滿足期望值417
                }
            }

        }
        else
        {
            //如果驗證不通過,則返回401錯誤,並且Body中寫入錯誤原因
            actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, new HttpError("Token 不正確"));
        }

    }

token驗證

 //驗證token
    public bool ValidateToken(string loginTime,string token)
    {
        bool flag = true;
        DateTime checkTime = DateTime.Parse(loginTime);

        //先驗證時間是否過期
        DateTime nowtime = DateTime.Now;

        TimeSpan a = nowtime - checkTime;

        Logging.Error("a:"+ a.TotalSeconds);

        if (a.TotalSeconds > 120)//時間過期
        {
            flag = false;
        }
        else
        {
            string checkToken = Utils.GetTokenString(loginTime);
            Logging.Error("1:"+checkToken+";2:"+token);
            //比較token
            if (token.Equals(checkToken, StringComparison.CurrentCultureIgnoreCase))
            {
                flag = true;
            }
            else
            {
                flag = false;
            }

        }
        return flag;
    }

測試請求授權

  1. 新建一個Contorller,新增一個名為Test的Action進行測試
[HttpGet]
    [Authorize]
    [Route("Test")]
    //public string Test()
    public WxResponseResultModel Test()
    {
        WxResponseResultModel rsEntity = new WxResponseResultModel();
        rsEntity.Code = "200";
        rsEntity.Message = "這是後臺傳的測試方法";
        //return "這是後臺傳的測試方法";
        return rsEntity;
    }
  1. 前端頁面請墳,在本機新建html頁面進行測試,token使用了MD5加密方式。
 var keyStr = '123456';
    var timestamp = getMyFormatDate(new Date(),'yyyy-MM-dd hh:mm:ss');//獲取當前時間
    console.log("timestamp:" + timestamp);
    var token = hexMD5(keyStr + timestamp);
    console.log("token:" + token);
    var apiServiceBaseUri = "http://localhost:52545/";
    $(function () {
        var data = {code:"25"};
        $.ajax({
            beforeSend: function (xhr) {
                xhr.setRequestHeader('Authorization', 'BasicAuth ' + timestamp+"|"+token);//token規則
            },
            url: apiServiceBaseUri + 'Login/Test',
            type: "GET",
            dataType: 'json',
            success: function (data) {
                alert(data.Message);
                //alert(Message);
            }
        });
    });

首先測試沒有[Authorize]的時候,因為最先執行的就是AuthorizationFilter,所以毫無懸念會進入OnAuthorization()進行驗證。

其次,在Test這個Action新增[Authorize]看看,這裡會有一個疑問,明明是應該認證的方法,添加了[Authorize]屬性,更加應該進入OnAuthorization()才對,為什麼會拒絕認證呢?

這是因為是配置檔案中,配置了全域性過濾器。

//註冊全域性Filter
        config.Filters.Add(new AuthFilterAttribute());
把[Authorize]換成配置的[AuthFilter]屬性,就可以成功訪問了。

釋出IIS,聯合小程式測試

由於小程式對ajax這一塊進行了封裝,請求統一使用 wx.request請求,使用wx.request加入報文報求的時候,不像ajax這樣,寫在beforeSend裡面。wx.request是寫在header裡面。token規則在app.js裡面做了全域性變數呼叫。

 wx.request({
         url: app.globalData.api + 'Login/Test',
         method: "GET",
         header: { 
         'Authorization': app.globalData.header,
         'content-type': 'application/json',
        }, // 設定請求的 header
        success: function (res) {
        //如果是物件的話,寫法為
          console.log(res.data.Message);
         // console.log(res.data);
        },
        fail:function(res){
         console.log("fail:" + res)
       }
  });

測試期間出現一個bug,提示如下: 未能找到 CodeDom 提供程式型別“Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.3.0, Culture=neutral, PublicKeyToken=31bf385