我们可以为servicestack设置多个AuthProviders。但他们都应该使用一个用户存储库。有没有办法设置一个基本的auth提供程序和两个不同的存储库用于不同的路由?
答案 0 :(得分:5)
简短的回答是,你不能简单地使用多个存储库。它不支持这一点的原因是因为CredentialsProvider
使用AppHost依赖性容器(IoC)来解析IUserAuthRepository
,所以它只需要一个存储库,此外,还需要有关路线中使用哪个存储库的更多信息。
您可以编写自己的CredentialsProvider
,然后编写一个新的BasicAuthProvider
,但这对于简单的基本身份验证来说是很多工作。如果您使用InMemoryAuthRepository
或RedisAuthRepository
,您会发现即使您创建了单独的实例,存储库实际上也会合并,因为它们使用相同的缓存键。 :(
// This doesn't work. The repositories will merge.
var repository1 = new InMemoryAuthRepository();
repository1.CreateUserAuth(new UserAuth { Id = 1, UserName = "cburns", FullName = "Charles Montgomery Burns" }, "excellent");
repository1.CreateUserAuth(new UserAuth { Id = 2, UserName = "bartsimpson", FullName = "Bart Simpson" }, "Ay caramba");
repository1.CreateUserAuth(new UserAuth { Id = 3, UserName = "homersimpson", FullName = "Homer J. Simpson" }, "donuts");
var repository2 = new InMemoryAuthRepository();
repository2.CreateUserAuth(new UserAuth { Id = 1, UserName = "thehulk", FullName = "The Hulk" }, "pebbles");
repository2.CreateUserAuth(new UserAuth { Id = 2, UserName = "captainamerican", FullName = "Captain America" }, "redwhiteblue");
repository2.CreateUserAuth(new UserAuth { Id = 3, UserName = "spiderman", FullName = "Spider Man" }, "withgreatpower");
ServiceStack具有很好的可扩展性,您可以轻松地对自己的身份验证进行角色扮演。 Basic Auth是一个非常简单的协议。
我只是创建了这个RequestFilterAttribute
,允许您使用任意数量的自定义存储库。
Full Source Code Here ServiceStack v4自托管应用
简单的自定义存储库。 您可以使用更复杂的存储库,并包含数据库查找等。但这很简单,仅用于演示目的:
public class MyUserRepository
{
public string Name { get; set; }
public Dictionary<string, string> Users { get; set; }
public MyUserRepository(string name, Dictionary<string, string> users = null)
{
Name = name;
Users = users ?? new Dictionary<string, string>();
}
}
RequestFilterAttribute
。 即。进行身份验证
public class BasicAuthAttribute : RequestFilterAttribute {
readonly string _realmName;
readonly string _repositoryName;
public BasicAuthAttribute(string realmName, string repositoryName = null)
{
_realmName = realmName;
_repositoryName = repositoryName ?? realmName;
}
public override void Execute(IRequest req, IResponse res, object requestDto)
{
// Get the correct repository to authenticate against
var repositories = HostContext.TryResolve<MyUserRepository[]>();
MyUserRepository repository = null;
if(repositories != null)
repository = repositories.FirstOrDefault(r => r.Name == _repositoryName);
// Determine if request has basic authentication
var authorization = req.GetHeader(HttpHeaders.Authorization);
if(repository != null && !String.IsNullOrEmpty(authorization) && authorization.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
// Decode the credentials
var credentials = Encoding.UTF8.GetString(Convert.FromBase64String(authorization.Substring(6))).Split(':');
if(credentials.Length == 2)
{
// Try and match the credentials to a user
var password = repository.Users.GetValueOrDefault(credentials[0]);
if(password != null && password == credentials[1])
{
// Credentials are valid
return;
}
}
}
// User requires to authenticate
res.StatusCode = (int)HttpStatusCode.Unauthorized;
res.AddHeader(HttpHeaders.WwwAuthenticate, string.Format("basic realm=\"{0}\"", _realmName));
res.EndRequest();
}
}
用法:它的用法很简单。使用属性
装饰您的操作方法或DTOpublic static class TestApp
{
[Route("/TheSimpsons", "GET")]
public class TheSimpsonsRequest {}
[Route("/Superheros", "GET")]
public class SuperherosRequest {}
public class TestController : Service
{
[BasicAuth("The Simpsons", "Simpsons")] // Requires a 'Simpsons' user
public object Get(TheSimpsonsRequest request)
{
return new { Town = "Springfield", Mayor = "Quimby" };
}
[BasicAuth("Superheros")] // Requires a user from 'Superheros'
public object Get(SuperherosRequest request)
{
return new { Publishers = new[] { "Marvel", "DC" } };
}
}
}
[BasicAuth( string realmName, string repositoryName)]
realmName
是要在身份验证对话框中显示的名称repositoryName
是要查找凭据的存储库的名称。它是可选的,如果排除它将使用realmName
作为存储库名称。 设置该演示使用AppHost
Configure
方法静态配置存储库:
public override void Configure(Funq.Container container)
{
container.Register<MyUserRepository[]>(c => new[]
{
new MyUserRepository("Simpsons", new Dictionary<string, string> {
{ "cburns", "excellent" },
{ "bartsimpson", "Ay caramba" },
{ "homersimpson", "donuts" }
}),
new MyUserRepository("Superheros", new Dictionary<string, string> {
{ "thehulk", "pebbles" },
{ "captainamerica", "redwhiteblue" },
{ "spiderman", "withgreatpower" }
})
});
}
测试:当您导航到/TheSimpsons
时,服务会提示输入“辛普森一家”凭据,但此处不会允许“超级英雄”凭据。当你去/Superheros
时,情况正好相反。
我感谢此解决方案确实偏离了ServiceStack身份验证提供程序。如上所述,可以使用ServiceStack身份验证提供程序从头开始构建您自己的身份验证,但这将很困难,超出了StackOverflow的范围。如果要执行此操作,可以通读现有提供程序并确定如何完成此操作。但是我的建议是,如果你不需要那么复杂,那么请从上面的例子开始。