ServiceStack.NET authentification Windows (NTLM) dans ASP.NET MVC
comment implémenter L'authentification Windows dans un projet ServiceStack ASP.NET MVC4?
j'ai commencé avec un request-Filter global ajouté dans le 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();
});
}
cela ouvre une boîte de dialogue de connexion, qui si saisie correctement (nom d'utilisateur existe et mot de passe valide est entré et aussi le myTestUser
est défini à la présente), les résultats d'une réponse positive.
Si quelque chose ne va pas, la boîte de dialogue de connexion s'affiche à nouveau. -- C'est des sons ok pour moi.
Mais après avoir retapé le bon utilisateur dans cette deuxième fenêtre de connexion, il s'arrête de fonctionner. La boîte de dialogue s'ouvre à nouveau, si elle est à nouveau incorrecte. Aucun point de rupture n'est atteint à l'intérieur de la fonction filter.
une idée de ce qui pourrait causer ça?
c'est ce que j'ai ajouté dans le web.config:
<authentication mode="Windows"/>
<authorization>
<deny users="?" /> <!--only allow authenticated users-->
</authorization>
je veux complètement verrouiller le site web et permettre l'accès aux utilisateurs windows spécifiés dans la base de données seulement avec leurs permissions spécifiques (rôles). Je dois mettre en œuvre la logique personnalisée pour accéder à la " liste des utilisateurs et rôle." Peut-être qu'il y a une autre façon de le faire dans MVC4/ ASP.NET?
4 réponses
ServiceStack d'Authentification Personnalisé pour Windows Intranets
je me suis battu toute la journée et j'ai trouvé ce qui suit.
d'Abord le cas d'utilisation:
vous êtes sur un intranet d'entreprise en utilisant L'authentification Windows. Vous configurez le mode d'authentification = "Windows" dans votre web.config et c'est tout!
votre stratégie est la suivante:
vous ne savez pas qui est l'utilisateur parce qu'ils ne sont pas dans votre table des utilisateurs ou groupe ActiveDirectory ou autre. Dans ce cas, vous leur donnez le rôle de "guest" et vous ajustez L'UI en conséquence. Peut-être leur donner un lien de courriel pour demander l'accès.
vous avez l'utilisateur dans votre liste d'utilisateurs mais ils n'ont pas été assignés un rôle. Donnez-leur donc le rôle de l ' "utilisateur" et réduisez L'UI comme ci-dessus. Peut-être qu'ils peuvent voir leurs trucs, mais rien d'autre.
L'utilisateur est dans votre liste et a été assigné à un rôle. Initialement vous assignerez le rôle en mettant à jour manuellement la table UserAuth dans la base de données. Finalement, vous aurez un service qui le fera pour les utilisateurs autorisés.
alors passons au code.
Côté Serveur
dans la couche de Service ServiceStack, nous créons un fournisseur D'autorisation personnalisé selonhttps://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);
}
}
Et voici notre méthode personnalisée:
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;
}
dans appHost Configure ajouter les responsables suivants à la fin de la fonction
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));
ceci envoie quelques en-têtes supplémentaires vers le bas au client de sorte que nous pouvons couper L'UI selon le rôle de l'utilisateur.
Côté Client
côté client lorsque nous adressons une première demande au serveur, nous affichons un nom D'Utilisateur et un mot de passe tel que requis par L'authentification personnalisée. Les deux sont positionnés à "NotRequired" comme nous saurons qui le l'utilisateur est du côté du serveur via HttpContext.Courant.Utilisateur.Identité.Nom.
les utilisations suivantes AngularJS pour AJAX comms.
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');
});
};
il est probablement intéressant de noter que depuis la version 4.0.21. Un fournisseur d'authentification windows a été implémenté comme suit: https://github.com/ServiceStack/ServiceStack/blob/master/release-notes.md#windows-auth-provider-for-aspnet
UPDATE consultez la note de @BiffBaffBoff ci-dessous. On dirait que Windows Auth s'est fait avoir.
j'ai implémenté un simple fournisseur D'Auth NTLM. Si j'ai un peu de temps, je vais l'emballer dans un plugin et publier sur GitHub. Mais pour l'instant:
web.config - ceci empêche les utilisateurs anonymes de se connecter (comme indiqué dans la question):
<system.web>
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
...
</system.web>
Simple wrapper autour de CredentialsAuthProvider - une partie de la une autre logique d'auth requiert soit des justificatifs D'identité, soit un condensé, et c'était la plus facile à utiliser comme base:
public class NTLMAuthProvider : CredentialsAuthProvider
{
public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
{
return !string.IsNullOrWhiteSpace(session.UserName);
}
}
Et pre-test filter regarder l'identité fournie par 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);
}
});
Avez-vous activé l'emprunt d'identité dans le système.élément web de votre fichier de configuration?
<identity impersonate="true"/>
cela pourrait mener au second échec, si quelque chose essaie d'accéder à une ressource restreinte.
vous avez mentionné vouloir mettre en œuvre la logique personnalisée pour identifier les utilisateurs qui pourraient accéder au système. Je suppose que
<allow roles="DomainName\WindowsGroup" />
<deny users="*" />
n'est pas suffisant. Si elle est grande, mais sinon, vous pouvez également mettre en place un fournisseur de rôle personnalisé qui pourrait vous aider. Cela nécessite d'utiliser l'authentification de formulaires au lieu de Windows, mais cela ne signifie pas nécessairement que vous ne pouvez pas utiliser les justificatifs D'identité de Windows pour authentifier vos utilisateurs - cela signifie Juste que vous devrez faire un peu de la lourde charge vous-même.