且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

从Angular JS使用Web API令牌身份验证时出现错误请求(400)

更新时间:2022-06-19 07:04:30

好的,这将是一个很长的答案,但请坚持到最后:)

步骤1:删除Global.asax

在Owin管道上运行时,不需要Global.asax.我会说Startup.cs是Owins Global.asax. 它们基本上达到了相同的目的,因此继续进行删除.

The Global.asax is not needed when you run on the Owin pipeline. The Startup.cs is what I would say Owins Global.asax. They basically fills the same purpose so go ahead and remove it.

步骤2:在WebApiConfig.cs中删除Cors处理

不需要此代码,因为您已经在Startup.cs中声明了它.

This code is not needed as you already declare it in the Startup.cs.

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

您的WebApiConfig.cs将如下所示

Your WebApiConfig.cs will then look like this

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的Owin管道中添加Web Api和承载令牌自动验证

您无需将WebApiConfig绑定到Global.asax中,而是将其附加到管道中. 还将不记名令牌处理应用于管道.

Instead of binding the WebApiConfig in the Global.asax you attach it to the pipeline. Also apply the bearer token handling to the pipeline.

您的Startup.cs将如下所示

Your Startup.cs will then look like this

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所做的回调.海事组织,我认为诺言更容易阅读

The request to the oauth server need to be of content type x-www-form-urlencoded which basically is a string. I also added promise instead of callbacks which is what the $q does. IMO I think the promise is more clear to read

提示:不要以明文形式发送凭据. 您可以使用btoa(password)将它们解码为Base64字符串,然后在后端对其进行解码.

TIP: don't send the credentials in clear text. You could endecode them to Base64 string with btoa(password) and then decode it in your backend.

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);
                }
            );;
    }
}]);

就是这样.