1

我想使用Angular JS作爲客戶端來建立Web API令牌驗證。我對Web API中的令牌認證概念非常陌生。使用來自Angualr JS的Web API令牌驗證時的錯誤請求(400)

我不想使用ASP.NET身份默認表來添加或認證用戶。我有我自己的數據庫和一個名爲「EmployeeAccess」表的表,其中包含EmployeeNumber作爲用戶ID和密碼。我想根據此表中的值對用戶進行身份驗證,然後想要授予令牌,以便他們獲得後續調用的授權。我已經使用了所有必需的OWIN和ASP.NET引用來實現結果。這裏是我的不同組件的代碼: -

的Global.asax

public class WebApiApplication : System.Web.HttpApplication 
    { 
     protected void Application_Start() 
     { 
      // AreaRegistration.RegisterAllAreas(); 
      GlobalConfiguration.Configure(WebApiConfig.Register); 

     } 

     protected void Application_BeginRequest() 
     { 
      if (HttpContext.Current.Request.HttpMethod == "OPTIONS") 
      { 
       // Cache the options request. 
       HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", HttpContext.Current.Request.Headers["Origin"]); 
       HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, PUT, DELETE, POST, OPTIONS"); 
       HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization"); 
       HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000"); 
       HttpContext.Current.Response.End(); 
      } 
     } 
    } 

WebApiConfig.cs

public static class WebApiConfig 
    { 
     public static void Register(HttpConfiguration config) 
     {    
      // Web API routes 
      config.MapHttpAttributeRoutes(); 

      config.Routes.MapHttpRoute(
       name: "DefaultApi", 
       routeTemplate: "api/{controller}/{id}", 
       defaults: new { id = RouteParameter.Optional } 
      ); 

      config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); 
      config.Formatters.Remove(config.Formatters.XmlFormatter); 

      var cors = new EnableCorsAttribute("*", "*", "*"); 
      config.EnableCors(cors); 
     } 
    } 

Startup.cs

[assembly: OwinStartup(typeof(Application.WebAPI.Startup))] 

namespace Application.WebAPI 
{ 
    public class Startup 
    { 
     public void Configuration(IAppBuilder app) 
     { 
      // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888 
      app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 
      var myProvider = new AuthorizationServerProvider(); 
      OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions 
      { 
       AllowInsecureHttp = true, 
       TokenEndpointPath = new PathString("/Token"), 
       AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
       Provider = myProvider 
      }; 
      app.UseOAuthAuthorizationServer(options); 
     } 
    } 
} 

AuthorizationServerProvider.cs

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider 
    { 
     public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
     { 
      context.Validated(); // 
     } 

     public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
     { 
      //context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 
      string userId = context.UserName; 
      string password = context.Password; 

      EmployeeAccessBLL chkEmpAccessBLL = new EmployeeAccessBLL(); 
      EmployeeAccessViewModel vmEmployeeAccess = chkEmpAccessBLL.CheckEmployeeAccess(Convert.ToInt32(userId), password); 

      if(vmEmployeeAccess != null) 
      { 
       var identity = new ClaimsIdentity(context.Options.AuthenticationType); 
       identity.AddClaim(new Claim("username", vmEmployeeAccess.EmpName)); 
       context.Validated(identity); 
      } 
      else 
      { 
       context.SetError("invalid_grant", "Provided username and password is incorrect"); 
       return; 
      } 
     }    
    } 

的login.html

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8" /> 
    <title></title> 
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script> 
    <script src="../Scripts/AngularControllers/LoginController.js"></script> 
    <script src="../Scripts/AngularServices/ApiCallService.js"></script> 
</head> 
<body ng-app="appHome"> 
    <div ng-controller="ctrlLogin"> 
     <label>Employee Number</label> 
     <input type="text" id="txtEmpNumber" ng-model="md_empnumber" /> 
     <br/> 
     <br/> 
     <label>Password</label> 
     <input type="text" id="txtEmpNumber" ng-model="md_password" /> 

     <button id="btnAdd" type="submit" ng-click="Login()">Login</button> 
    </div> 
</body> 
</html> 

LoginController.js

var myApp = angular.module('appHome', []); 
myApp.controller("ctrlLogin", ['$scope', 'MetadataOrgFactory', '$location', function ($scope, MetadataOrgFactory, $location) { 
    $scope.Login = function() { 
     var objLogin = { 
      'username' : $scope.md_empnumber, 
      'password' : $scope.md_password, 
      'grant_type' : 'password' 
     }; 

     MetadataOrgFactory.postLoginCall('Token', objLogin, function (dataSuccess) { 
      alert("Welcome " + dataSuccess);   
     }, function (dataError) { 
     }); 
    } 
}]); 

ApiCallService.js

var appService = angular.module('appHome'); 
appService.factory('MetadataOrgFactory', ['$http', function ($http) { 

    var url = 'http://localhost:60544';  
    var dataFactory = {};  
    dataFactory.postLoginCall = function (controllerName, objData, callbackSuccess, callbackError) { 

     $http.post(url + '/' + controllerName, objData,{headers:{ 'Content-Type': 'application/x-www-form-urlencoded' }}).then 
      (function success(response) { 
       alert("Success"); 
       callbackSuccess(response.data); 
      }, function error(response) { 
       callbackError(response.status); 
      }); 
    }; 
    return dataFactory; 
}]) 

當我點擊登錄按鈕,然後我收到以下錯誤信息: -

POST http://localhost:60544/Token 400(錯誤請求)

當我調試的WebAPI代碼,然後我發現「AuthorizationServerProvider.cs」中的方法「GrantResourceOwnerCredentials()」永遠不會被執行。錯誤消息在此之前。只有方法「ValidateClientAuthentication」和「MatchEndpoint」被執行。

請幫助我成功運行Web API令牌驗證場景。如果任何代碼被發現是多餘的,請讓我知道,這樣我就可以刪除它。

回答

8

好吧,這將是一個長期的答案,但堅持到最後:)

第1步:刪除在Global.asax

不需要在Global.asax當您運行在歐文管道上。 Startup.cs就是我所說的Owins Global.asax。 他們基本上填充相同的目的,所以繼續前進並將其刪除。

步驟2:刪除CORS廣告在WebApiConfig.cs

因爲你已經在Startup.cs聲明它,則不需要此代碼處理。然後

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 

你WebApiConfig.cs看起來像這樣

public static class WebApiConfig 
{ 
    public static void Register(HttpConfiguration config) 
    {    
     // Web API routes 
     config.MapHttpAttributeRoutes(); 
     config.Routes.MapHttpRoute(
      name: "DefaultApi", 
      routeTemplate: "api/{controller}/{id}", 
      defaults: new { id = RouteParameter.Optional } 
     ); 
     config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); 
     config.Formatters.Remove(config.Formatters.XmlFormatter); 
    } 
} 

第3步:網絡API和承載令牌autentication而是添加到Owin管道在Startup.cs

結合將Global.asax中的WebApiConfig附加到管道中。 也將承載令牌處理應用於管道。然後

你Startup.cs看起來像這樣

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 
     OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions 
     { 
      AllowInsecureHttp = true, 
      TokenEndpointPath = new PathString("/Token"), 
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
      Provider = new AuthorizationServerProvider() 
     }; 
     app.UseOAuthAuthorizationServer(options); 
     app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); 

     //Register the web api to the pipeline 
     HttpConfiguration config = new HttpConfiguration(); 
     WebApiConfig.Register(config); 
     app.UseWebApi(config); 
    } 
} 

第4步:頭以請求添加在AuthorizationServerProvider.cs

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider 
{ 
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
    { 
     context.Validated(); 
    } 
    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
    { 
     SetContextHeaders(context); 
     string userId = context.UserName; 
     string password = context.Password; 

     EmployeeAccessBLL chkEmpAccessBLL = new EmployeeAccessBLL(); 
     EmployeeAccessViewModel vmEmployeeAccess = chkEmpAccessBLL.CheckEmployeeAccess(Convert.ToInt32(userId), password); 

     if(vmEmployeeAccess != null) 
     { 
      var identity = new ClaimsIdentity(context.Options.AuthenticationType); 
      identity.AddClaim(new Claim("username", vmEmployeeAccess.EmpName)); 
      context.Validated(identity); 
     } 
     else 
     { 
      context.SetError("invalid_grant", "Provided username and password is incorrect"); 
      return; 
     } 
    } 
    private void SetContextHeaders(IOwinContext context) 
    { 
     context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 
     context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET, PUT, DELETE, POST, OPTIONS" }); 
     context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Content-Type, Accept, Authorization" }); 
     context.Response.Headers.Add("Access-Control-Max-Age", new[] { "1728000" }); 
    } 
} 

第5步:做一個正確的請求到Oauth服務器

對oauth服務器的請求需要的內容類型x-www-form-urlencoded它基本上是一個字符串。 我還添加了promise,而不是$ q所做的回調。國際海事組織我認爲承諾更清晰可讀

提示:不要以明文發送憑據。 您可以使用btoa(密碼)將它們編碼爲Base64字符串,然後在後端將其解碼。

angular.module('appHome').factory('MetadataOrgFactory', ['$http', function ($http) { 

    var url = 'http://localhost:60544';  
    var dataFactory = {}; 
    dataFactory.login = function (userName, password) { 
     var deferred = $q.defer(); 

     $http({ 
      method: 'POST', 
      url: url + '/Token', 
      processData: false, 
      contentType: 'application/x-www-form-urlencoded', 
      data: "grant_type=password&username=" + userName + "&password=" + password, 
     }). 
      success(function (data) { 
       deferred.resolve(data); 
      }). 
      error(function (message, status) {    
       console.log(message); 
       deferred.reject(message, status); 
      }); 

     return deferred.promise; 
    }; 
    return dataFactory; 
}]); 

第6步:從您的控制器

angular.module('appHome', []); 
angular.module('appHome').controller("ctrlLogin", ['$scope', 'MetadataOrgFactory', '$location', function ($scope, MetadataOrgFactory, $location) { 
    $scope.Login = function() { 

     MetadataOrgFactory.postLoginCall($scope.md_empnumber, $scope.md_password).then(
      function (result) { 
       //success 
      }, 
       function (error, statusCode) { 
        console.log(error); 
       } 
      );; 
    } 
}]); 

就是這樣做的登錄請求。

+0

感謝Marcus爲您精心編寫答案,幫助像我這樣的新手獲得最基本的答案。我希望我可以給你+10,但該網站不允許.. :-) – user1843970

+0

@ user1843970 Np,很高興幫助!祝你有美好的一天 –

相關問題