2013-08-21 6 views
5

У меня возникла проблема с решением архитектуры приложения ASP MVC, которое обрабатывает html-страницы и веб-службы через ServiceStack.ServiceStack API и ASP MVC Аутентификация двумя способами

Приложение работает в базовом URL-адресе, например «http://myapplication.com», а SS живет в «http://myapplication.com/api», потому что это самый простой способ настроить оба.

В целом все работает нормально, но когда я достиг части авторизации и аутентификации, я застрял.

Для этого мне нужны файлы cookie приложения, так как ASP обычно выполняет FormsAuthentication, и пользователи будут проходить через экран входа в систему и могут использовать действия и контроллеры, если используется атрибут «Авторизовать». Это типично для ASP, поэтому у меня нет проблем с этим, например «http://myapplication.com/PurchaseOrders».

С другой стороны, клиенты моего приложения будут использовать мою веб-службу api из javascript. Эти веб-службы также будут отмечены в некоторых случаях атрибутом «Аутентификация» ServiceStack. Например, «http://myapplication.com/api/purchaseorders/25» должен будет проверить, может ли пользователь просмотреть этот конкретный заказ на поставку, в противном случае отправить 401 Unauthorized, чтобы javascript мог обрабатывать эти случаи и отображать сообщение об ошибке.

И последнее, но не менее важное: другая группа пользователей будет использовать мой API через токен, используя любое внешнее приложение (возможно, Java или .NET). Поэтому мне нужно решить два типа аутентификации: один с использованием имени пользователя и пароля, другой - с помощью токена и сделать их постоянными, поэтому, как только они будут аутентифицированы в первый раз, следующие вызовы быстрее решаются из API.

Это код, который у меня есть до сих пор, я очень просто прояснил пример.

[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(); 
    } 

Как для проверки подлинности я использую этот класс

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

О функции Настройки из AppHost Я настраиваю свой собственный класс аутентификации, чтобы использовать его в качестве значения по умолчанию. Думаю, я должен создать еще один класс и добавить его здесь, чтобы обработать сценарий токенов.

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

До сих пор ServiceStack работает должным образом. Я могу отправить сообщение в/auth/credentials, передавая имя пользователя и пароль, и он хранит эту информацию, поэтому следующий вызов службы, на который уже разрешен запрос, отлично!

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

Пожалуйста, не указывайте мне учебные пособия по ServiceStack, я был там последние два дня и до сих пор не могу понять это.

Большое спасибо.

ответ

3

Вот что я обычно использую:

Вы можете заменить метод действия "входа" на код ниже:

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

...и плагины:

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

.... провайдер 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); 
    } 
} 

... наконец, утилита регистрации класса

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

У меня нет компьютера с VS, чтобы проверить ваш ответ, хотя он выглядит хорошо. Попробует сегодня вечером. Благодаря! – coffekid

+0

Это действительно работало. Большое спасибо! – coffekid