2013-08-21 1 views
5

Je rencontre des difficultés pour résoudre l'architecture d'une application ASP MVC qui gère les pages html et les services Web via ServiceStack. L'application vit dans l'URL de base, par exemple "http://myapplication.com" et SS dans "http://myapplication.com/api" parce que c'est la manière la plus simple de configurer les deux.API ServiceStack et authentification ASP MVC de deux manières

En général tout fonctionne bien, mais quand j'ai atteint la partie de l'autorisation et l'authentification, est où je suis coincé. Pour un, j'ai besoin de l'application gérer les cookies comme ASP normalement fait par FormsAuthentication, et les utilisateurs passeraient par un écran de connexion et pourraient consommer des actions et des contrôleurs lorsque l'attribut "Autoriser" est utilisé. Ceci est typique d'ASP, donc je n'ai aucun problème avec celui-ci, comme "http://myapplication.com/PurchaseOrders". En revanche, les clients de mon application vont consommer mon API de service Web à partir de javascript. Ces services Web seront également étiquetés dans certains cas avec l'attribut "Authenticate" de ServiceStack. Par exemple "http://myapplication.com/api/purchaseorders/25" devra valider si l'utilisateur peut voir cette commande particulière, sinon envoyer un 401 non autorisé afin que javascript puisse gérer ces cas et afficher le message d'erreur.

Enfin et surtout, un autre groupe d'utilisateurs utilisera mon API par un token, en utilisant n'importe quelle application externe (probablement Java ou .NET). J'ai donc besoin de résoudre deux types d'authentification, l'un en utilisant le nom d'utilisateur et le mot de passe, l'autre par le jeton et de les rendre persistants. Une fois authentifiés la première fois, les prochains appels sont plus rapides à résoudre depuis l'API.

C'est le code que j'ai jusqu'à présent, je l'ai mis très simplement pour clarifier l'exemple.

[HttpPost] 
    public ActionResult Logon(LogOnModel model, string returnUrl) 
    { 
     if (ModelState.IsValid) 
     { 
      JsonServiceClient client = new JsonServiceClient("http://myapplication.com/api/"); 
      var authRequest = new Auth { provider = CredentialsAuthProvider.Name, UserName = model.UserName, Password = model.Password, RememberMe = model.RememberMe }; 
      try 
      { 

       var loginResponse = client.Send(authRequest); 

       FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(loginResponse.UserName, false, 60); 
       var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket)); 
       Response.Cookies.Add(cookie); 

       if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\")) 
       { 
        return Redirect(returnUrl); 
       } 
       else 
       { 
        return RedirectToAction("Index", "Test"); 
       } 

      } 
      catch (Exception) 
      { 
       ModelState.AddModelError("", "Invalid username or password"); 
      } 
     } 

     return View(); 
    } 

En ce qui concerne le fournisseur d'authentification J'utilise cette classe

public class MyCredentialsAuthProvider : CredentialsAuthProvider 
{ 
    public MyCredentialsAuthProvider(AppSettings appSettings) 
     : base(appSettings) 
    { 

    } 

    public override bool TryAuthenticate(IServiceBase authService, string userName, string password) 
    { 
     //Add here your custom auth logic (database calls etc) 
     //Return true if credentials are valid, otherwise false 
     if (userName == "testuser" && password == "nevermind") 
     { 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 

    public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo) 
    { 
     //Fill the IAuthSession with data which you want to retrieve in the app eg: 
     session.FirstName = "some_firstname_from_db"; 
     //... 

     session.CreatedAt = DateTime.Now; 
     session.DisplayName = "Mauricio Leyzaola"; 
     session.Email = "[email protected]"; 
     session.FirstName = "Mauricio"; 
     session.IsAuthenticated = true; 
     session.LastName = "Leyzaola"; 
     session.UserName = "mauricio.leyzaola"; 
     session.UserAuthName = session.UserName; 
     var roles = new List<string>(); 
     roles.AddRange(new[] { "admin", "reader" }); 
     session.Roles = roles; 

     session.UserAuthId = "uniqueid-from-database"; 

     //base.OnAuthenticated(authService, session, tokens, authInfo); 

     authService.SaveSession(session, SessionExpiry); 
    } 
} 

Sur la fonction Configurer des apphost Je suis en train de ma classe d'authentification personnalisé pour l'utiliser comme la valeur par défaut. Je suppose que je devrais créer une autre classe et l'ajouter ici, pour gérer le scénario du jeton.

  Plugins.Add(new AuthFeature(() => new CustomUserSession(), 
      new IAuthProvider[] { 
       new MyCredentialsAuthProvider(appSettings) 
      }, htmlRedirect: "~/Account/Logon")); 

Jusqu'à présent, ServiceStack fonctionne comme prévu. Je peux envoyer un message à/auth/credentials en passant le nom d'utilisateur et le mot de passe et il stocke cette information, donc, lors d'un prochain appel à un service, la requête est déjà autorisée, super jusqu'à présent!

La question que je dois savoir est comment appeler (et probablement placer quelque part dans SS) l'utilisateur qui se connecte à partir de mon contrôleur de compte. Si vous voyez le premier bloc de code que j'essaie d'appeler le service Web (semble que je le fais mal) et cela fonctionne, mais le prochain appel à n'importe quel service Web semble non authentifié. S'il vous plaît ne me pointez pas vers les tutoriels ServiceStack, je suis là depuis deux jours et je n'arrive toujours pas à le comprendre.

Merci beaucoup d'avance.

Répondre

3

Voici ce que je l'habitude d'utiliser:

Vous pouvez remplacer la méthode d'action "d'ouverture de session" avec le code ci-dessous:

public ActionResult Login(LogOnModel model, string returnUrl) 
    { 
     if (ModelState.IsValid) 
     { 
      try 
      { 
       var authService = AppHostBase.Resolve<AuthService>(); 
       authService.RequestContext = System.Web.HttpContext.Current.ToRequestContext(); 
       var response = authService.Authenticate(new Auth 
       { 
        UserName = model.UserName, 
        Password = model.Password, 
        RememberMe = model.RememberMe 
       }); 

       // add ASP.NET auth cookie 
       FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); 

       return RedirectToLocal(returnUrl); 
      } 
      catch (HttpError) 
      { 
      } 
     } 

     // If we got this far, something failed, redisplay form 
     ModelState.AddModelError("", "The user name or password provided is incorrect."); 
     return View(model); 
    } 

...et les plug-ins:

  //Default route: /auth/{provider} 
      Plugins.Add(new AuthFeature(() => new CustomUserSession(), 
      new IAuthProvider[] { 
       new CustomCredentialsAuthProvider(), 
       new CustomBasicAuthProvider() 
      })); 

.... les classes de fournisseur Auth:

public class CustomCredentialsAuthProvider : CredentialsAuthProvider 
{ 
    public override bool TryAuthenticate(IServiceBase authService, string userName, string password) 
    { 
     return UserLogUtil.LogUser(authService, userName, password); 
    } 
} 

public class CustomBasicAuthProvider : BasicAuthProvider 
{ 
    public override bool TryAuthenticate(IServiceBase authService, string userName, string password) 
    { 
     return UserLogUtil.LogUser(authService, userName, password); 
    } 
} 

... enfin, la classe utilitaire de journalisation

internal static class UserLogUtil 
{ 
    public static bool LogUser(IServiceBase authService, string userName, string password) 
    { 
     var userService = new UserService(); //This can be a webservice; or, you can just call your repository from here 
     var loggingResponse = (UserLogResponse)userService.Post(new LoggingUser { UserName = userName, Password = password }); 

     if (loggingResponse.User != null && loggingResponse.ResponseStatus == null) 
     { 
      var session = (CustomUserSession)authService.GetSession(false); 
      session.DisplayName = loggingResponse.User.FName.ValOrEmpty() + " " + loggingResponse.User.LName.ValOrEmpty(); 
      session.UserAuthId = userName; 
      session.IsAuthenticated = true; 
      session.Id = loggingResponse.User.UserID.ToString(); 

      // add roles and permissions 
      //session.Roles = new List<string>(); 
      //session.Permissions = new List<string>(); 
      //session.Roles.Add("Admin); 
      //session.Permissions.Add("Admin"); 

      return true; 
     } 
     else 
      return false; 
    } 
} 
+0

Je ne dispose pas d'un ordinateur VS pour tester votre réponse, même si ça a l'air bien. Je vais essayer ce soir. Merci! – coffekid

+0

Cela a fonctionné réellement. Merci beaucoup! – coffekid