5

我在解决通过 ServiceStack 为 html 页面和 Web 服务提供服务器的 ASP MVC 应用程序的体系结构时遇到问题。

应用程序位于基本 URL 中,例如“ http://myapplication.com ”,SS 位于“ http://myapplication.com/api ”中,因为这是配置两者的最简单方法。

一般来说,一切正常,但是当我到达授权和身份验证的部分时,我就陷入了困境。

一方面,我需要应用程序处理 cookie,因为 ASP 通常通过 FormsAuthentication 进行操作,并且用户将通过登录屏幕并在使用“授权”属性时可以使用操作和控制器。这是典型的 ASP,所以我对它没有任何问题,例如“ http://myapplication.com/PurchaseOrders ”。

另一方面,我的应用程序的客户端将从 javascript 使用我的 Web 服务 api。在某些情况下,这些 Web 服务也将使用 ServiceStack 的“Authenticate”属性进行标记。例如,“ 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 = "mauricio.leyzaola@gmail.com";
        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 中的某处设置)从我的帐户控制器登录的用户。如果您看到我正在尝试调用 Web 服务的第一个代码块(看起来我做错了)并且它可以工作,但是对任何 Web 服务的下一次调用看起来未经身份验证。

请不要将我指向 ServiceStack 教程,我过去两天一直在那里,但仍然无法弄清楚。

提前非常感谢。

4

1 回答 1

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

....身份验证提供程序类:

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;
    }
}
于 2013-08-22T15:22:44.250 回答