ServiceStack.asp.net Windows身份验证(NTLM)净MVC

本文关键字:NTLM MVC 身份验证 asp net Windows ServiceStack | 更新日期: 2023-09-27 18:02:44

如何在基于ASP的ServiceStack项目中实现Windows身份验证。净MVC4吗?

我开始在AppHost中添加一个全局请求过滤器:

private void ConfigureAuth(Funq.Container container)
{
    this.RequestFilters.Add((httpReq, httpResp, requestDto) =>
    {
        var user = HttpContext.Current.User.Identity;
        if (!user.IsAuthenticated ||
            !user.Name.Contains(_myTestUser)) //todo: check username here in database (custom logic) if it has access to the application
            httpResp.ReturnAuthRequired();
    });
}

打开一个登录对话框,如果输入正确(用户名存在,输入有效密码,并且myTestUser设置为此),则会产生成功的响应。如果有任何错误,将再次显示登录对话框。——听起来不错。但是在第二个登录窗口重新输入正确的用户后,它停止工作。对话框再次打开,如果它再次不正确。在过滤器函数内部没有碰到断点。

你知道是什么原因导致的吗?

这是我在web.config:

中添加的内容
<authentication mode="Windows"/>
<authorization>
  <deny users="?" /> <!--only allow authenticated users-->
</authorization>

我想完全锁定网站,并使访问指定的windows用户在数据库中只与他们的特定权限(角色)。我需要实现自定义逻辑来访问"用户和角色列表"。也许在MVC4/ASP.NET中有其他方法可以做到这一点?

ServiceStack.asp.net Windows身份验证(NTLM)净MVC

Windows intranet的ServiceStack自定义身份验证

我一整天都在思考这个问题,得出了以下结论。

第一个用例:

您在使用Windows身份验证的企业内部网。您在web中设置身份验证模式="Windows"。配置,就是这样!

你的策略是:

  1. 你不知道用户是谁,因为他们不在你的用户表或ActiveDirectory组或其他什么。在这种情况下,您将赋予它们"来宾"的角色,并相应地修改UI。或者给他们一个邮件链接请求访问。

  2. 用户列表中有该用户,但该用户未被分配角色。所以给他们一个"用户"的角色,并像上面那样修改UI。也许他们可以看到他们的东西,但没有其他。

  3. 用户在您的列表中,并且已被分配角色。最初,您将通过手动更新数据库中的UserAuth表来分配角色。最终你会有一个服务可以为授权用户做这些。

我们来看看代码

服务器端

在ServiceStack服务层中,我们按照https://github.com/ServiceStack/ServiceStack/wiki/Authentication-and-authorization

创建一个自定义凭据授权提供者。
      public class CustomCredentialsAuthProvider : CredentialsAuthProvider
        {
            public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
            {
                //NOTE: We always authenticate because we are always a Windows user! 
                // Yeah, it's an intranet  
                return true;
            }
            public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
            {
                // Here is why we set windows authentication in web.config
                var userName = HttpContext.Current.User.Identity.Name;
                //  Strip off the domain
                userName = userName.Split('''')[1].ToLower();
                // Now we call our custom method to figure out what to do with this user
                var userAuth = SetUserAuth(userName);
                // Patch up our session with what we decided
                session.UserName = userName;
                session.Roles = userAuth.Roles;            
                // And save the session so that it will be cached by ServiceStack 
                authService.SaveSession(session, SessionExpiry);
            }
        }

这是我们的自定义方法:

     private UserAuth SetUserAuth(string userName)
            {
                // NOTE: We need a link to the database table containing our user details
                string connStr = ConfigurationManager.ConnectionStrings["YOURCONNSTRNAME"].ConnectionString;
                var connectionFactory = new OrmLiteConnectionFactory(connStr, SqlServerDialect.Provider);
                // Create an Auth Repository
                var userRep = new OrmLiteAuthRepository(connectionFactory);
                // Password not required. 
                const string password = "NotRequired";
                // Do we already have the user? IE In our Auth Repository
                UserAuth userAuth = userRep.GetUserAuthByUserName(userName);
                if (userAuth == null ){ //then we don't have them}
                // If we don't then give them the role of guest
                userAuth.Roles.Clear();
                userAuth.Roles.Add("guest")
                // NOTE: we are only allowing a single role here               
                // If we do then give them the role of user
                // If they are one of our team then our administrator have already given them a role via the setRoles removeRoles api in ServiceStack
               ...
                // Now we re-authenticate out user
                // NB We need userAuthEx to avoid clobbering our userAuth with the out param
                // Don't you just hate out params?
                // And we re-authenticate our reconstructed user
                UserAuth userAuthEx;
                var isAuth = userRep.TryAuthenticate(userName, password, out userAuthEx);
                return userAuth;
            }

在appHost Configure中,在函数末尾添加以下ResponseFilters

    ResponseFilters.Add((request, response, arg3) => response.AddHeader("X-Role",request.GetSession(false).Roles[0]));
    ResponseFilters.Add((request, response, arg3) => response.AddHeader("X-AccountName", request.GetSession(false).UserName));

这将发送一些额外的头向下到客户端,以便我们可以根据用户的角色修剪UI。

客户端

在客户端,当我们向服务器发出第一个请求时,我们根据自定义身份验证的要求发布用户名和密码。两者都设置为"norequired",因为我们将通过HttpContext.Current.User.Identity.Name知道用户在服务器端是谁。

下面的代码使用AngularJS进行AJAX通信。

    app.run(function($templateCache, $http, $rootScope) {
        // Authenticate and get X-Role and X-AccountName from the response headers and put it in $rootScope.role
        // RemeberMe=true means that the session will be cached 
        var data={"UserName" : "NotRequired", "Password" : "NotRequired", "RememberMe": true };
        $http({ method : 'POST', url : '/json/reply/Auth', data : data }).
            success(function (data, status, headers, config) {
            // We stash this in $rootScope for later use!
                $rootScope.role = headers('X-Role');
                $rootScope.accountName = headers('X-AccountName');
                console.log($rootScope.role);
                console.log($rootScope.role);
            }).
            error(function (data, status, headers, config) {
                // NB we should never get here because we always authenticate
                toastr.error('Not Authenticated'n' + status, 'Error');
            });
    };

值得注意的是,自4.0.21版本以来。一个windows身份验证提供程序已经实现,如下所示:https://github.com/ServiceStack/ServiceStack/blob/master/release-notes.md#windows-auth-provider-for-aspnet

UPDATE查看下面@BiffBaffBoff的注释。看来Windows Auth又来了。

我实现了一个非常简单的NTLM验证提供程序。如果我有一些时间,我会把它包装在一个插件和发布在GitHub上。但是现在:

网络。config -这可以防止匿名用户连接(如问题所指出的):

<system.web>
  <authentication mode="Windows" />
  <authorization>
    <deny users="?" />
  </authorization>
...
</system.web>

对CredentialsAuthProvider的简单包装—其他一些验证逻辑需要凭据或摘要,这是最容易使用的基础:

public class NTLMAuthProvider : CredentialsAuthProvider
{
    public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
    {
        return !string.IsNullOrWhiteSpace(session.UserName);
    }
}

先决条件过滤器查看IIS提供的标识:

        this.PreRequestFilters.Add((req, resp) =>
        {
            IAuthSession session = req.GetSession();
            if (session.UserName == null)
            {
                session.UserName = ((HttpRequestWrapper)req.OriginalRequest).LogonUserIdentity.Name;
                // Add permissions & roles here - IUserAuthRepository, ICacheClient, etc.
                req.SaveSession(session);
            }
        });

您是否在系统中启用了模拟?配置文件的Web元素?

<identity impersonate="true"/>

这可能导致第二次失败,如果有东西试图访问受限制的资源。

您提到想要实现自定义逻辑来标识哪些用户可以访问系统。我假设

<allow roles="DomainName'WindowsGroup" />
<deny users="*" />

不充分。如果是,那就太好了,否则,您也可以实现一个自定义角色提供程序,它可能会对您有所帮助。这就需要使用Forms身份验证而不是Windows,但这并不一定意味着你不能使用Windows凭据来验证你的用户——这只是意味着你必须自己做一点繁重的工作。