net mvc implement asp application active c# asp.net-mvc-4 servicestack windows-authentication

c# - implement - Autenticación de Windows ServiceStack.NET(NTLM) en ASP.NET MVC



windows authentication c# web application (4)

Autenticación personalizada de ServiceStack para intranets de Windows

He estado bateando mi cabeza contra esto todo el día y he encontrado lo siguiente.

Primero el caso de uso:

Usted está en una intranet corporativa usando la autenticación de Windows. Configura el modo de autenticación = "Windows" en su web.config y eso es todo.

Tu estrategia es esta:

  1. No sabe quién es el usuario porque no está en su tabla de usuarios o grupo de ActiveDirectory o lo que sea. En este caso, les asigna el papel de "invitado" y recorta la interfaz de usuario en consecuencia. Tal vez darles un enlace de correo electrónico para solicitar acceso.

  2. Tiene al usuario en su lista de usuarios, pero no se les ha asignado un rol. Entonces dales el rol de "usuario" y recorta la IU como se indica arriba. Tal vez puedan ver sus cosas pero nada más.

  3. El usuario está en su lista y se le ha asignado un rol. Inicialmente, asignará el rol actualizando manualmente la tabla de UserAuth en la base de datos. Eventualmente, tendrá un servicio que hará esto para los usuarios autorizados.

Así que vamos al código.

Lado del servidor

En la capa de ServiceStack Service creamos un Proveedor de Autorización de Credenciales Personalizadas según 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); } }

Y aquí está nuestro método personalizado:

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

En appHost Configure agregue los siguientes ResponseFilters al final de la función

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

Esto envía algunos encabezados adicionales al cliente para que podamos recortar la interfaz de usuario según la función del usuario.

Lado del cliente

En el lado del cliente, cuando enviamos la primera solicitud al servidor, POSTAMOS un nombre de usuario y una contraseña, tal como lo requiere la autenticación personalizada. Ambos están configurados en "NotRequired" ya que sabremos quién es el usuario en el lado del servidor a través de HttpContext.Current.User.Identity.Name.

Lo siguiente usa AngularJS para comunicaciones 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''); }); };

¿Cómo implementar la autenticación de Windows en un proyecto de ServiceStack en ASP.NET MVC4?

Comencé con un filtro de solicitud global agregado en el 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(); }); }

Esto abre un cuadro de diálogo de inicio de sesión, que si se ingresa correctamente (existe un nombre de usuario y se ingresa una contraseña válida y también se configura myTestUser ), da como resultado una respuesta exitosa. Si algo está mal, el diálogo de inicio de sesión se muestra de nuevo. - Eso suena bien para mí. Pero después de volver a escribir el usuario correcto en esa segunda ventana de inicio de sesión, deja de funcionar. El cuadro de diálogo se abre de nuevo, si es igual de nuevo, es incorrecto. No se alcanza ningún punto de interrupción dentro de la función de filtro.

¿Alguna idea de lo que podría causar esto?

Eso es lo que agregué en el web.config:

<authentication mode="Windows"/> <authorization> <deny users="?" /> <!--only allow authenticated users--> </authorization>

Quiero bloquear completamente el sitio web y permitir el acceso a usuarios de Windows específicos en la base de datos solo con sus permisos (roles) específicos. Necesito implementar lógica personalizada para acceder a la "lista de usuarios y roles". Tal vez hay otra manera de hacer esto en MVC4 / ASP.NET?


¿Ha habilitado la suplantación en el elemento system.web de su archivo de configuración?

<identity impersonate="true"/>

Eso podría llevar a la segunda falla, si algo intenta acceder a un recurso restringido.

Usted mencionó que desea implementar una lógica personalizada para identificar qué usuarios podrían acceder al sistema. yo asumo eso

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

No es suficiente. Si lo es, excelente, pero de lo contrario, también puede implementar un proveedor de roles personalizado que pueda ayudarlo. Esto requiere el uso de la autenticación de Formularios en lugar de Windows, pero eso no significa necesariamente que no pueda usar las credenciales de Windows para autenticar a sus usuarios, solo significa que tendrá que hacer un poco de esfuerzo usted mismo.



ACTUALIZACIÓN Echa un vistazo a la nota de @BiffBaffBoff a continuación. Parece que Windows Auth se ha incorporado.

Implementé un proveedor de autenticación NTLM bastante simple. Si tengo algo de tiempo, envolveré esto en un complemento y lo publicaré en GitHub. Pero por ahora:

web.config : esto evita que los usuarios anónimos se conecten (como se señala en la pregunta):

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

Envoltorio simple alrededor de CredentialsAuthProvider - Algunas de las otras lógicas de autenticación requieren Credenciales o Compendio, y esta fue la más fácil de usar como base:

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

Y un filtro de solicitud previa para ver la identidad proporcionada por 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); } });