我在解决通过ServiceStack服务html页面和Web服务的ASP MVC应用程序的体系结构时遇到了麻烦。
该应用程序位于基本网址中,例如“http://myapplication.com”,而SS位于“http://myapplication.com/api”中,因为这是配置两者的最简单方法。
总的来说一切正常,但当我到达授权和身份验证的一部分时,就是我被困住的地方。
首先,我需要应用程序处理cookie,因为ASP通常会执行FormsAuthentication,并且当使用属性“Authorize”时,用户将通过登录屏幕并使用操作和控制器。这是典型的ASP,所以我没有问题,例如“http://myapplication.com/PurchaseOrders”。
另一方面,我的应用程序的客户端将从javascript使用我的Web服务api。在某些情况下,还会使用ServiceStack的“Authenticate”属性标记这些Web服务。例如,“http://myapplication.com/api/purchaseorders/25”必须验证用户是否可以查看该特定采购订单,否则发送401 Unauthorized,以便javascript可以处理这些情况并显示错误消息。
最后但并非最不重要的是,另一组用户将使用任何外部应用程序(可能是Java或.NET)通过令牌使用我的API。因此,我需要解决两种类型的身份验证,一种使用用户名和密码,另一种使用令牌并使其持久化,因此一旦第一次进行身份验证,下一次调用就可以更快地从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 /凭证提交一个帖子,它会存储这些信息,所以下次调用服务请求已经被授权了,到目前为止很棒!
我需要知道的问题是如何调用(并且可能在SS中的某个位置)从我的帐户控制器登录的用户。如果您看到我尝试调用Web服务的第一个代码块(看起来我做错了)并且它可以工作,但是对任何Web服务的下一次调用看起来都是未经身份验证的。
请不要指向ServiceStack教程,过去两天我一直在那里,仍然无法理解。
提前多多感谢。
答案 0 :(得分: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;
}
}