Подтвердить что ты не робот

Проверка подлинности Windows ServiceStack.NET(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?

4b9b3361

Ответ 1

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

Я весь день моргнул головой об этом и придумал следующее.

Сначала используется прецедент:

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

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

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

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

  • Пользователь находится в вашем списке и ему была назначена роль. Первоначально вы назначите роль, вручную обновив таблицу UserAuth в базе данных. В конце концов у вас будет служба, которая сделает это для авторизованных пользователей.

Итак, перейдите в код.

Сторона сервера

На уровне ServiceStack Service мы создаем Поставщик авторизации пользовательских учетных данных в соответствии с 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 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');
            });
    };

Ответ 3

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

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

web.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);
    }
}

И фильтр prequequest, чтобы посмотреть идентификатор, предоставленный 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);
            }
        });

Ответ 4

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

<identity impersonate="true"/>

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

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

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

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