ServiceStack.NET Проверка подлинности Windows (NTLM) в ASP.NET MVC

Как реализовать аутентификацию Windows в проекте ServiceStack, построенном на ASP.NET 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?


person MVCible    schedule 06.03.2013    source источник
comment
Можете посмотреть в Event Viewer? В журналах Windows › Приложение Попробуйте подключиться снова и посмотрите, есть ли у вас ошибка/предупреждение/информация с вашим приложением ASP.NET.   -  person Joffrey Kern    schedule 08.03.2013
comment
Это может быть не то, что вы сделали после, но в Application_Start() в Global.asax вы можете поместить глобальный фильтр для авторизации всего, например: GlobalFilters.Filters.Add(new System.Web.Mvc.AuthorizeAttribute()); Я мало что знаю о ServiceStack, но я считаю, что это работает лучше, чем возиться с web.config и т. д.   -  person user1166905    schedule 19.03.2013


Ответы (4)


Пользовательская аутентификация ServiceStack для интрасетей Windows

Целый день ломал голову над этим и пришел к следующему.

Сначала вариант использования:

Вы находитесь в корпоративной интрасети, используя проверку подлинности Windows. Вы устанавливаете режим аутентификации = "Windows" в своем web.config, и все!

Ваша стратегия такова:

  1. Вы не знаете, кто этот пользователь, потому что он не входит в вашу таблицу пользователей, группу ActiveDirectory или что-то еще. В этом случае вы даете им роль «гостя» и соответствующим образом обрезаете пользовательский интерфейс. Может быть, дайте им ссылку на электронную почту, чтобы запросить доступ.

  2. У вас есть пользователь в списке пользователей, но ему не назначена роль. Поэтому дайте им роль «пользователя» и обрежьте пользовательский интерфейс, как указано выше. Может быть, они и видят свои вещи, но больше ничего.

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

Это отправляет некоторые дополнительные заголовки клиенту, чтобы мы могли обрезать пользовательский интерфейс в соответствии с ролью пользователя.

Сторона клиента

На стороне клиента, когда мы отправляем первый запрос на сервер, мы отправляем имя пользователя и пароль, как того требует пользовательская аутентификация. Для обоих установлено значение «NotRequired», поскольку мы будем знать, кто пользователь на стороне сервера, через 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');
            });
    };
person pjsvis    schedule 27.03.2013

Наверное, стоит отметить, что начиная с версии 4.0.21. Поставщик проверки подлинности Windows был реализован, как показано здесь: https://github.com/ServiceStack/ServiceStack/blob/master/release-notes.md#windows-auth-provider-for-aspnet

person CallumVass    schedule 01.06.2014

ОБНОВЛЕНИЕ Ознакомьтесь с примечанием @BiffBaffBoff ниже. Похоже, Windows Auth был включен.

Я реализовал довольно простой провайдер аутентификации NTLM. Если у меня будет время, я завершу это в плагин и опубликую на GitHub. Но сейчас:

web.config — это предотвращает подключение анонимных пользователей (как указано в вопросе):

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

Простая оболочка вокруг CredentialsAuthProvider. Некоторая другая логика аутентификации требует либо Credentials, либо Digest, и это было проще всего использовать в качестве основы:

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);
            }
        });
person jklemmack    schedule 31.03.2014

Вы включили олицетворение в элементе system.web файла конфигурации?

<identity impersonate="true"/>

Это может привести ко второму сбою, если что-то попытается получить доступ к ограниченному ресурсу.

Вы упомянули о желании реализовать пользовательскую логику для определения того, какие пользователи могут получить доступ к системе. я предполагаю, что

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

недостаточно. Если это так, отлично, но в противном случае вы также можете реализовать поставщик настраиваемых ролей это может тебе помочь. Это требует использования проверки подлинности с помощью форм вместо проверки подлинности Windows, но это не обязательно означает, что вы не можете использовать учетные данные Windows для проверки подлинности своих пользователей — это просто означает, что вам придется выполнять небольшую тяжелую работу самостоятельно.

person Steve Westbrook    schedule 19.03.2013