【问题标题】:How do I use my custom ServiceStack authentication provider with Redis?如何将我的自定义 ServiceStack 身份验证提供程序与 Redis 一起使用?
【发布时间】:2014-01-08 13:19:29
【问题描述】:

我已经为我的身份验证实现了一个自定义CredentialsAuthProvider,并将其与内存会话存储中的默认值一起使用。

现在我尝试将会话存储更改为 Redis,并将其添加到 AppHost 中的 Configure() 方法中:

container.Register<IRedisClientsManager>(c => 
    new PooledRedisClientManager("localhost:6379"));

container.Register<ICacheClient>(c => (ICacheClient)c
    .Resolve<IRedisClientsManager>()
    .GetCacheClient()).ReusedWithin(Funq.ReuseScope.None);

现在,当我进行身份验证时,我可以看到带有 urn:iauthsession:... 的密钥已添加到我的 Redis 服务器。但是所有带有[Authenticate] 属性的路由都会给出401 Unauthorized 错误。

CustomCredentialsAuthProvider 是这样实现的:

public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
    public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
    {
        if (userName != string.Empty && password != string.Empty)
        {
            // Database call ...
            var session = (CustomSession)authService.GetSession();
            session.ClientId = login.ClientId;
            // Fill session...
            authService.SaveSession(session, SessionExpiry);
            return true;
        }
        return false;
    }
}

ServiceStack 版本:3.9.71

编辑:

我试图覆盖 CredentialsAuthProvider IsAuthorized 方法但没有成功。

但是我从AuthUserSession 继承了我的会话对象,它也有一个IsAuthorized 方法。当我从此方法返回 true 时,Redis 会话确实使用 Authenticate 属性。

public class CustomSession : AuthUserSession
{
    public int ClientId { get; set; }
    ...

    public override bool IsAuthorized(string provider)
    {
        return true;
    }
}

【问题讨论】:

  • 我认为这与您的问题无关,但您不应该在 TryAuthenticate 方法中填写会话。首选做法是在OnAuthenticated 方法中。 See here。从AuthenticationAttribute 中的 Redis 检索会话似乎存在问题。要对此进行调试,我将从您的一个操作处理程序中删除该属性并尝试在其中添加base.Request.GetSession(),然后查看您的会话是否在您的服务中正确恢复。
  • @Scott 当我删除[Authenticate] 属性时,我可以在操作处理程序中检索会话。所以这似乎是AuthenticationAttribute中的一个错误?
  • 这不是一个错误,您的实现有问题。我认为您需要设置 session.UserAuthNamesession.UserAuthId 除非您覆盖 CredentialProviders IsAuthorized 方法。
  • 谢谢@Scott,但我没有让它与 CredentialsProvider 一起工作。请参阅我更新的问题。
  • 只是查看您的代码。删除public ICacheClient cache { get; set; } ICacheClient 已在您继承的Service 中设置。您可以拨打base.Cache。仍在寻找其他问题。

标签: redis servicestack servicestack-bsd servicestack-auth


【解决方案1】:

Authenticate 属性调用AuthUserSession 类的IsAuthorized。 在我的情况下,为了使它与 Redis 缓存客户端一起工作,我已经完成了以下操作

public override bool IsAuthorized(string provider)
{
    string sessionKey = SessionFeature.GetSessionKey(this.Id);
    ICacheClient cacheClient = AppHostBase.Resolve<ICacheClient>();

    CustomUserSession session = cacheClient.Get<CustomUserSession>(sessionKey);

    if (session == null)
    {
        return false;
    }

    return session.IsAuthenticated;
}

【讨论】:

    【解决方案2】:

    我想不出办法让 [Authenticate] 属性与 Redis 存储一起使用。

    我不得不写一个自定义的[SessionAuth] 属性

    public class SessionAuthAttribute : RequestFilterAttribute
    {
        public ICacheClient cache { get; set; }
        public string HtmlRedirect { get; set; }
    
        public SessionAuthAttribute()
        {
        }
    
        public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
        {
            string sessionId = req.GetSessionId();
            if (string.IsNullOrEmpty(sessionId))
            {
                HandleNoSession(req, res);
    
            }
            else
            {
                var session = cache.Get<CustomSession>("urn:iauthsession:" + sessionId);
                if (session == null || !session.IsAuthenticated)
                {
    
                    HandleNoSession(req, res);
                }
            }
        }
    
        private void HandleNoSession(IHttpRequest req, IHttpResponse res)
        {
    
            if (req.ResponseContentType.MatchesContentType(MimeTypes.Html))
            {
    
                res.RedirectToUrl(HtmlRedirect);
                res.End();
    
            }
            res.StatusCode = (int)HttpStatusCode.Unauthorized;
            res.Write("not authorized");
            res.Close();
        }
    }
    

    在我的 AppHost Configure() 方法中,我只注册了 SessionFeature 和 IRedisClientsManager/ICacheClient:

    Plugins.Add(new SessionFeature());
    
    container.Register<IRedisClientsManager>(c => new PooledRedisClientManager("localhost:6379"));
    
    container.Register<ICacheClient>(c => (ICacheClient)c.Resolve<IRedisClientsManager>()
            .GetCacheClient()).ReusedWithin(Funq.ReuseScope.None);
    

    CustomSession 类继承自 AuthUserSession:

    public class CustomSession : AuthUserSession
    {
        public int ClientId { get; set; }
        ...
    }
    

    我在 /login/auth 上有一个用于身份验证部分的正常服务路由和一个用于删除会话的 /login/logout 路由:

    public class LoginService : Service
    {
        public ICacheClient cache { get; set; }
    
        public object Post(AuthRequest request)
        {
            string userName = request.UserName;
            string password = request.Password;
    
            // check login allowed
    
            if (IsAllowed)
            {
    
                var session = SessionFeature.GetOrCreateSession<CustomSession>(cache);
    
                session.ClientId = login.ClientId;
                ...
                session.IsAuthenticated = true;
                session.Id = SessionFeature.GetSessionId();
    
                this.SaveSession(session, TimeSpan.FromSeconds(30 * 60));
    
    
                return true;
            }
    
            return false;
        }
    
    
        [SessionAuth]
        public object Any(LogoutRequest request)
        {
            this.RemoveSession();
            return true;
        }
    }
    

    }

    我仍然对使用普通 [Authenticate] 属性的解决方案感兴趣。

    【讨论】:

      猜你喜欢
      • 2021-01-03
      • 2013-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-10
      • 2013-02-10
      • 2012-04-05
      相关资源
      最近更新 更多