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,很高兴帮助!祝你有美好的一天 –

相关问题