在客户端 Headers 或 Cookie 中使用 sessionId 时找不到 ServiceStack 用户 session

ServiceStack user session not found when using sessionId in client Headers or Cookies

我正在使用带有自定义身份验证的 ServiceStack v4。这是正确设置和工作。我可以调用 /auth 服务并获得返回的具有唯一 SessionId 的 AuthorizationResponse。

我还有 swagger-ui 插件设置。使用它,我可以通过 /auth 进行身份验证,然后毫无问题地调用我的其他需要身份验证的服务之一。

现在,从使用 c# JsonServiceClient 的辅助 MVC 应用程序,我可以再次成功调用 /auth,然后使用相同的客户端 object 保护服务。但是,如果我处理该客户端(在将唯一的 sessionId 保存到 cookie 之后),然后创建一个新客户端,并将 sessionId 添加为 Cookie 或通过 headers 按照记录使用 x-ss-pid,调用服务 returns 401。如果我调用 non-secure 服务,然后尝试访问唯一用户 session,它 returns一个新的session。

如果我查看该服务中的请求 headers,cookie 或 Header 显然设置了 sessionId。 sessionId 也存在于 session 缓存中。问题似乎是试图从请求中获取 session 的代码没有找到它。

更具体地说,ServiceExtensions.GetSessionId 似乎正在查看 HostContext 而不是调用请求。我不确定为什么。也许我在这里误解了一些东西。

如果我直接尝试使用以下代码获取我预期的 session,则发现没有问题。

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

那么,我是不是漏掉了一些明显的东西?或者在创建我的二级客户端时可能不是很明显?

客户端调用示例代码

这是我的授权码。它包含在控制器 class 中。这只是相关部分。

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

这是我的辅助客户端设置和服务调用,包含在它自己的控制器中 class 在 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());
}

额外更新

在身份验证过程中,从 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 值,但我不知道为什么以及在哪里设置它。此时我什至不明白 IRequest 上的 Items 容器是什么。因此,代码永远无法实现检查我的 cookie 或 headers

的功能

Session wiki 描述了 ServiceStack Session 使用的不同 cookie。

如果客户端想要使用一个永久的SessionId(即ss-pid),它还需要发送一个ss-opt=perm Cookie来表明它想要使用永久的Session。在身份验证期间使用 RememberMe=true 选项进行身份验证时会自动设置此 Cookie。

用于确保 Session Id 附加到当前请求的 Session RequestFilter 没有使用 public IRequest.GetPermanentSessionId() API's 也在 HTTP Headers 中查找 SessionId。此问题已解决 with this commit,现在允许您使用 HTTP Headers 发出 Session 请求,例如:

//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.

但是,如果我处理掉那个客户端(在将唯一的 sessionId 保存到 cookie 之后)

如果客户端被配置了,你保存sessionId的cookie在哪里? answer 可能会提供一些额外的信息。

然后稍后创建一个新客户端,并将 sessionId 添加为 Cookie 或通过 headers 使用 x-ss-pid 作为记录,调用服务 returns 401

如果您 store/save 一个有效的 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());
}