我想用Angular JS作为客户端建立Web API令牌认证。我对Web API中的令牌认证概念非常陌生。
我不想使用ASP.NET Identity默认表来添加或验证用户。我有自己的数据库和一个名为" 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代码时,我发现了方法&#34; GrantResourceOwnerCredentials()&#34;在&#34; AuthorizationServerProvider.cs&#34;永远不会被执行在此之前出现错误消息。只有方法&#34; ValidateClientAuthentication&#34;和&#34; MatchEndpoint&#34;被执行。
请帮我成功运行Web API令牌身份验证方案。如果发现任何代码冗余,请告诉我,以便我可以删除它。
答案 0 :(得分:11)
好的,这将是一个很长的答案,但坚持到最后:)
第1步:删除Global.asax
在Owin管道上运行时不需要Global.asax。 Startup.cs就是我所说的Owins Global.asax。 它们基本上填充了相同的目的,所以继续并删除它。
第2步:删除WebApiConfig.cs中的Cors处理
不需要此代码,因为您已在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:在Startup.cs中将Web Api和承载令牌身份验证添加到Owin管道
不是在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的作用。 IMO我认为这个承诺更加清晰
提示:请勿以明文形式发送凭据。 您可以使用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);
}
);;
}
}]);
那就是它。