我为我的服务实施了Basic Auth。由于ServiceStack的AuthFeature
与会话概念紧密结合,因此我实现了一个自定义RequestFilter
,它执行无状态基本身份验证(凭据在每个请求中都进入)。我们的身份验证策略在内部考虑角色和权限。
除了身份验证之外,我们还需要强制执行授权(例如,用户正在操纵他拥有的产品)。我们正在使用FluentValidation进行所有服务验证。
授权验证包括使用请求参数交叉检查身份验证数据。问题是,我应该在哪里放置BasicAuthRequestFilter
中生成的身份验证数据?我应该在高速缓存中键入值对,例如,将RequestContext
(或唯一标识请求范围的任何其他对象)与Authentication
对象相关联吗?
我可以在请求Dto中插入AuthData
,这可以直接在RequestFilter
处获得,但这会弄乱我们的服务合同设计。我们在一个单独的DLL中定义dtos,其中只定义了服务输入/输出细节。
有什么建议吗? 提前谢谢。
答案 0 :(得分:4)
我也使用自己的自定义身份验证机制,并为我的服务提供自定义角色信息。我通过在自定义ServiceRunner
中对请求进行身份验证来执行此操作,然后可以将信息直接传递到我的自定义Service
基础。这最终意味着访问有关用户权限的信息非常容易。
创建自定义ServiceRunner
:
public class ServiceRunner<T> : ServiceStack.ServiceHost.ServiceRunner<T>
{
public ServiceRunner(IAppHost appHost, ActionContext actionContext) : base(appHost, actionContext)
{
}
public override object Execute(IRequestContext requestContext, object instance, T request)
{
// Check if the instance is of type AuthenticatedBase
var authenticatedBase = instance as AuthenticatedBase;
// If the request is not using the AuthenticatedBase, then allow it to run, as normal.
if(authenticatedBase == null)
return base.Execute(requestContext, instance, request);
/*
* Authentication required. Do you authorization check here.
* i.e.
* var authorization = requestContext.GetHeader("Authorization");
* bool authorised = ... some condition;
*/
/* You have access to your service base so if you injected the Db connection
* in you app config using IoC, then you can access the Db here.
* i.e.
* authenticatedBase.Db
*/
/*
* Not authorized?
* throw new UnauthorizedException();
*/
/*
* If authorized:
* Then simple set the details about their permissions
*/
authenticatedBase.AuthData = new AuthData { Id = 123, Roles = [], Username = "" };
// Pass back the authenticated base
return base.Execute(requestContext, authenticatedBase, request);
}
}
通过将应用程序添加到AppHost
:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new ServiceRunner<TRequest>(this, actionContext);
}
创建一个自定义类来保存您的身份验证数据,即用户会话信息,例如:
public class AuthData
{
public int Id { get; set; }
public string Username { get; set; }
public int[] Roles { get; set; }
...
}
然后创建自定义服务基础
public class AuthenticatedBase : Service
{
public AuthData AuthData { get; set; }
}
然后在服务中使用AuthData只是扩展AuthenticatedBase
。
public class CustomerHandler : AuthenticatedBase
{
public object Get(ListCustomers request)
{
// You can access the AuthData now in the handler
var roles = AuthData.Role; // Check they have the role required to list customers
...
}
}
您可能想知道为什么在ServiceRunner
上使用RequestFilter
会遇到麻烦,但主要优点是可以直接访问服务基础的实例,这是不可用的到RequestFilter。
RequestFilter
在服务基础实例化之前运行,因此您无法从那里填充它。 See order of operations for more information
通过访问ServiceBase,我们可以填充值(在本例中为AuthData
),并且我们可以访问注入的依赖项,例如数据库连接。
我希望你觉得这很有用。您应该能够将大部分现有RequestFilter复制到服务运行器中。如果您需要任何进一步的帮助,请告诉我。
由于您无法避免使用属性方法来处理身份验证需求,因此您仍然可以使用此方法:
在您现有的身份验证机制中,使用req.Items.Add
设置AuthData
,即其中req
是您的请求对象
req.Items.Add("AuthData", new AuthData { Username = "", Roles = [] ... });
然后访问服务基地中的AuthData
项目:
public class AuthenticatedBase : Service
{
public AuthData AuthData
{
get { return base.Request.Items["AuthData"] as AuthData; }
}
}