【问题标题】:ASP.NET MVC5/AngularJS/Web API app using Windows Authentication and OWIN使用 Windows 身份验证和 OWIN 的 ASP.NET MVC5/AngularJS/Web API 应用程序
【发布时间】:2015-01-03 13:41:04
【问题描述】:

我可能会让事情变得过于复杂,但我们有一个内部 ASP.NET MVC5 SPA 和 AngularJS,使用 Windows 身份验证。此应用程序有一个 SQL 后端数据库,该数据库有一个用户表,其中包含他们的帐户名以及他们在应用程序中各自的角色。我们将调用另一个也启用了 Windows 身份验证的 Web API 应用程序。

我曾尝试研究如何使用 OWIN 处理授权,但找不到任何有关 OWIN 和 Windows 身份验证的具体示例。出现的所有内容都使用带有用户名和密码的表单身份验证。

如何为我的应用程序使用 OWIN 和 Windows Auth?这是我的 OAuthAuthorizationServerProvider 类的示例。

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

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

        var container = UnityHelper.GetContainerInstance("***");
        var securityHelper = container.Resolve<ISecurityHelper>();

        User currentUser = securityHelper.GetCurrentUser(); // Validates user based on HttpContext.Current.User
        if (currentUser == null)
        {
            context.SetError("invalid_grant", "The user could not be found.");
            return;
        }

        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        identity.AddClaim(new Claim("sub", currentUser.AccountName));
        identity.AddClaim(new Claim("role", "user"));

        context.Validated(identity);
    }
}

更新: 糟糕,我忘了提供更多关于我们想要完成的任务的信息。如果可能,我们希望使用不记名身份验证票证,这样我们就不必在每次调用 Web api 方法时查找用户及其角色。

更新 2: 根据 Andrew 的要求,下面是我的 _securityHelper 类的 TLDR 版本,特别是 GetCurrentUser() 方法。你会注意到我正在尝试调用:

HttpContext.Current.GetOwinContext().Request.User.Identity.Name

这总是为用户返回 null

public class SecurityHelper : ISecurityHelper
{
    private readonly ISecurityGroupController _securityGroupController;
    private readonly IUserController _userController;
    private readonly IEmployeeController _employeeController;
    private readonly IFieldPermissionController _fieldPermissionController;
    private readonly IOACController _oacController;

    public SecurityHelper(ISecurityGroupController securityGroupController,
        IUserController userController,
        IEmployeeController employeeController,
        IFieldPermissionController fieldPermissionController,
        IOACController oacController)
    {
        _securityGroupController = securityGroupController;
        _userController = userController;
        _employeeController = employeeController;
        _fieldPermissionController = fieldPermissionController;
        _oacController = oacController;
    }

    // ... other methods

    public User GetCurrentUser()
    {
        User user = _userController.GetByAccountName(HttpContext.Current.GetOwinContext().Request.User.Identity.Name);
        if (user != null)
        {
            List<OAC> memberships = _oacController.GetMemberships(user.SourceId).ToList();
            if (IsTestModeEnabled() && ((user.OACMemberships != null && user.OACMemberships.Count == 0) || user.OACMemberships == null))
            {
                user.OACMemberships = memberships;
            }
            else if (!IsTestModeEnabled())
            {
                user.OACMemberships = memberships;
            }
        }

        return user;
    }
}

【问题讨论】:

    标签: asp.net-mvc angularjs windows-authentication owin


    【解决方案1】:

    查看这篇文章,了解在 OWIN 中启用 Windows 身份验证的步骤: http://www.asp.net/aspnet/overview/owin-and-katana/enabling-windows-authentication-in-katana

    来自文章:

    Katana 目前不提供用于 Windows 身份验证的 OWIN 中间件,因为该功能已在服务器中可用。

    链接的文章介绍了为开发启用 Windows 身份验证。对于部署,这些设置位于身份验证下的 IIS 中。当用户第一次到达您的应用程序页面时,浏览器会提示他们输入用户名和密码。

    【讨论】:

    • 谢谢大卫。我已经在我的项目中禁用了匿名身份验证并启用了 Windows 身份验证,但我正在寻找更多关于如何在我的 SPA(角度应用程序)和 Web api 应用程序之间处理不记名令牌的指导。
    【解决方案2】:

    这个系列文章将是一个很好的起点:http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

    注意,将是以下代码,它本质上将不记名令牌存储在本地存储中并将其附加到标头。显然还有很多东西,包括表单和实际的服务器身份验证系统,但这应该会给你一个不错的开始。

    服务器组件:

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureOAuth(app);
        //Rest of code is here;
        }
    
        public void ConfigureOAuth(IAppBuilder app)
        {
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new SimpleAuthorizationServerProvider()
            };
    
            // Token Generation
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    
        }
    }
    

    以及以下客户端代码:

    'use strict';
    app.factory('authService', ['$http', '$q', 'localStorageService', function ($http, $q, localStorageService) {
    
        var serviceBase = 'http://ngauthenticationapi.azurewebsites.net/';
        var authServiceFactory = {};
    
        var _authentication = {
            isAuth: false,
            userName : ""
        };
    
        var _saveRegistration = function (registration) {
    
            _logOut();
    
            return $http.post(serviceBase + 'api/account/register', registration).then(function (response) {
                return response;
            });
    
        };
    
        var _login = function (loginData) {
    
            var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
    
            var deferred = $q.defer();
    
            $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
    
                localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });
    
                _authentication.isAuth = true;
                _authentication.userName = loginData.userName;
    
                deferred.resolve(response);
    
            }).error(function (err, status) {
                _logOut();
                deferred.reject(err);
            });
    
            return deferred.promise;
    
        };
    
        var _logOut = function () {
    
            localStorageService.remove('authorizationData');
    
            _authentication.isAuth = false;
            _authentication.userName = "";
    
        };
    
        var _fillAuthData = function () {
    
            var authData = localStorageService.get('authorizationData');
            if (authData)
            {
                _authentication.isAuth = true;
                _authentication.userName = authData.userName;
            }
    
        }
    
        authServiceFactory.saveRegistration = _saveRegistration;
        authServiceFactory.login = _login;
        authServiceFactory.logOut = _logOut;
        authServiceFactory.fillAuthData = _fillAuthData;
        authServiceFactory.authentication = _authentication;
    
        return authServiceFactory;
    }]);
    

    随着

    'use strict';
    app.factory('authInterceptorService', ['$q', '$location', 'localStorageService', function ($q, $location, localStorageService) {
    
        var authInterceptorServiceFactory = {};
    
        var _request = function (config) {
    
            config.headers = config.headers || {};
    
            var authData = localStorageService.get('authorizationData');
            if (authData) {
                config.headers.Authorization = 'Bearer ' + authData.token;
            }
    
            return config;
        }
    
        var _responseError = function (rejection) {
            if (rejection.status === 401) {
                $location.path('/login');
            }
            return $q.reject(rejection);
        }
    
        authInterceptorServiceFactory.request = _request;
        authInterceptorServiceFactory.responseError = _responseError;
    
        return authInterceptorServiceFactory;
    }]);
    

    【讨论】:

    • 谢谢安德鲁。正是那篇文章系列让我开始走上这条道路,并愿意实施它。我已经准备好了,但是当我尝试在 SimpleAuthorizationServerProvider.GrantResourceOwnerCredentials 中获取 HttpContext.Current.User 时,它为空,我无法获取主体。我已按照文章的建议将grant_type 设置为“密码”,但我无法获得委托人。
    • 你能把SecurityHelper类的代码贴出来吗?
    • 在尝试了一些事情之后,事实证明我对 Andrew 最初建议的文章系列的实现确实有效。我试图通过的“障碍”是从 HttpContext.Current.GetOwinContext().Request.User 获取用户主体。这在我的 GetCurrentUser() 方法中返回 null。好吧,事实证明,它返回 null 因为我正在使用 Fiddler 来编写我的请求并测试身份验证。一时兴起,我只是在我的 Angular 应用程序中创建了一个测试控制器来请求令牌,我的用户现在已被填充!
    • 为了进一步澄清,我的凭据没有使用 Fiddler 的作曲家传递,我认为这是一个完全不同的话题。
    • 很高兴听到您可以通过以下方式识别问题:)
    猜你喜欢
    • 2015-02-05
    • 1970-01-01
    • 2017-10-27
    • 2013-12-26
    • 1970-01-01
    • 2012-11-01
    • 1970-01-01
    • 2018-06-03
    • 2023-03-23
    相关资源
    最近更新 更多