我正在使用带有自定义身份验证的ServiceStack v4。这是设置和正常工作。我可以调用/ auth服务并获得具有唯一SessionId的返回AuthorizationResponse。
我也有swagger-ui插件设置。使用它,我可以通过/ auth进行身份验证,然后调用我需要身份验证的其他服务之一。
现在,从使用c#JsonServiceClient的辅助MVC应用程序,我可以再次成功调用/ auth,然后使用相同的客户端对象保护服务。但是,如果我处置该客户端(在将唯一的sessionId保存到cookie之后),则稍后创建一个新客户端,并将sessionId添加为Cookie或使用x-ss-pid添加标头,如调用服务返回401.如果我调用非安全服务,但随后尝试访问唯一用户会话,则返回新会话。
如果我查看该服务中的请求标头,则使用sessionId明确设置cookie或标头。 sessionId也存在于sessionCache中。问题似乎是试图从请求中获取会话的代码没有找到它。
更具体地说,似乎ServiceExtensions.GetSessionId正在查看HostContext而不是调用Request。我不确定为什么。也许我在这里误解了一些东西。
如果我直接尝试使用以下代码获取我预期的会话,则会发现它没有问题。
var req = base.Request;
var sessionId = req.GetHeader("X-" + SessionFeature.PermanentSessionId);
var sessionKey = SessionFeature.GetSessionKey(sessionId);
var session = (sessionKey != null ? Cache.Get<IAuthSession>(sessionKey) : null)?? SessionFeature.CreateNewSession(req, sessionId);
那么,我错过了一些明显的东西吗?或者在创建我的辅助客户端时可能不那么明显?
客户来电的示例代码
这是我的授权码。它包含在Controller类中。这只是相关部分。
using (var client = new JsonServiceClient(WebHelper.BuildApiUrl(Request)))
{
try
{
loginResult = client.Post(new Authenticate()
{
UserName = model.Email,
Password = model.Password,
RememberMe = model.RememberMe
});
Response.SetCookie(new HttpCookie(SessionFeature.PermanentSessionId, loginResult.SessionId));
return true;
}
}
这是我的辅助客户端设置和服务调用,包含在MVC应用程序的另一个区域中的自己的控制器类
using (var client = new JsonServiceClient(WebHelper.BuildApiUrl(Request)))
{
var cCookie = HttpContext.Request.Cookies.Get(SessionFeature.PermanentSessionId);
if (cCookie != null)
{
client.Headers.Add("X-" + SessionFeature.PermanentSessionId, cCookie.Value);
client.Headers.Add("X-" + SessionFeature.SessionOptionsKey, "perm");
}
response = client.Get(new SubscriptionStatusRequest());
}
其他更新
在Authenticate过程中,从HttpRequestExtensions调用以下函数,名称为= SessionFeature.PermanentSessionId
public static class HttpRequestExtensions
{
/// <summary>
/// Gets string value from Items[name] then Cookies[name] if exists.
/// Useful when *first* setting the users response cookie in the request filter.
/// To access the value for this initial request you need to set it in Items[].
/// </summary>
/// <returns>string value or null if it doesn't exist</returns>
public static string GetItemOrCookie(this IRequest httpReq, string name)
{
object value;
if (httpReq.Items.TryGetValue(name, out value)) return value.ToString();
Cookie cookie;
if (httpReq.Cookies.TryGetValue(name, out cookie)) return cookie.Value;
return null;
}
现在发生的是httpReq.Items包含一个SessionFeature.PermanentSessionId值,但我不知道为什么以及在哪里设置它。此时我甚至不理解Items容器在IRequest上的含义。因此,代码永远不会获得检查我的cookie或标题的功能
答案 0 :(得分:2)
Session wiki描述了ServiceStack Session使用的不同cookie。
如果客户端想要使用永久SessionId(即ss-pid
),它还需要发送ss-opt=perm
Cookie以表明它想要使用永久会话。在身份验证期间使用RememberMe=true
选项进行身份验证时,会自动设置此Cookie。
Session RequestFilter中存在一个问题,用于确保会话ID附加到当前请求时未使用公共IRequest.GetPermanentSessionId()
API,它也在HTTP标头中查找SessionIds。这已解决with this commit,现在可以使用HTTP标头创建会话请求,例如:
//First Authenticate to setup an Authenticated Session with the Server
var client = new JsonServiceClient(BaseUrl);
var authResponse = client.Send(new Authenticate
{
provider = CredentialsAuthProvider.Name,
UserName = "user",
Password = "p@55word",
RememberMe = true,
});
//Use new Client instance without Session Cookies populated
var clientWithHeaders = new JsonServiceClient(BaseUrl);
clientWithHeaders.Headers["X-ss-pid"] = authResponse.SessionId;
clientWithHeaders.Headers["X-ss-opt"] = "perm";
var response = clientWithHeaders.Send(new AuthOnly()); //success
此修补程序可从 v4.0.37 + 获得,现在为available on MyGet。
答案 1 :(得分:1)
但是,如果我处置该客户端(将唯一的sessionId保存到cookie后)
如果客户端被放置在哪里,您要保存sessionId的cookie?此answer可能会提供一些其他信息。
然后创建一个新客户端,并将sessionId添加为Cookie或使用x-ss-pid通过标头添加文档,调用服务返回401
如果您将有效的sessionId存储/保存为字符串,您应该能够在新客户端的CookieContainer中提供它(假设sessionId仍然经过身份验证)。我知道你说过你尝试将sessionId添加为Cookie但是我没有使用CookieContainer在你的问题中看到示例所以它应该看起来像......
using (var client = new JsonServiceClient(WebHelper.BuildApiUrl(Request)))
{
var cCookieId = savedCookieId; //a string that I believe you saved from a successfully authenticated client that is now disposed
if (cCookieId != null)
{
var cookie = new Cookie(SessionFeature.PermanentSessionId, cCookieId);
//cookie.Domian = "somedomain.com" //you will probably need to supply this as well
client.CookieContainer.Add(cookie)
}
response = client.Get(new SubscriptionStatusRequest());
}