随着越来越多的用户开始使用我们的网络服务,这个问题最近出现在我们的生产环境中。我对此服务进行身份验证的目标是在我们的数据库中拥有一个Web服务用户列表,每个用户都可以访问一个单独的数据库(并通过扩展名单独的连接字符串)。
我们有这样的布局: MasterDB Customer1DB Customer2DB Customer3DB 等。
在MasterDB中,除了需要使用的连接字符串之外,我们还存储了用于访问webservice的所有用户。我所经历的(我通过日志记录得出结论)是Customer1将登录,在创建dbFactory时将使用正确的连接字符串,但在我的服务类中使用dbFactory时,它将使用以前用户的数据库
我不确定如何处理这个问题,因为我对ServiceStack很缺乏经验。下面是我的Global.asax,我的CustomAuthUserSession类,以及一个看到此问题的示例服务:
配置中的Global.asax
private void HandleAuthentication(Funq.Container container)
{
var userRepo = GetUserRepository(container);
CreateUserAuthentications(userRepo);
}
private InMemoryAuthRepository GetUserRepository(Funq.Container container)
{
Plugins.Add(new AuthFeature(
() => new CustomAuthUserSession(container),
new IAuthProvider[] { new BasicAuthProvider(), }));
var userRepo = new InMemoryAuthRepository();
container.Register<IUserAuthRepository>(userRepo);
container.Register<ICacheClient>(new MemoryCacheClient());
return userRepo;
}
private void CreateUserAuthentications(InMemoryAuthRepository userRepo)
{
IDbConnectionFactory dbFactoryMaster = new OrmLiteConnectionFactory(
"Data Source=www.oursite.com;Initial Catalog=Master;User=User;Password=asdf1234;enlist=false;",
SqlServerOrmLiteDialectProvider.Instance);
using (IDbConnection db = dbFactoryMaster.OpenDbConnection())
{
try
{
var users = db.Select<tbl_WebServiceUsers>();
foreach (tbl_WebServiceUsers user in users)
{
tbl_Instances instance = db.Select<tbl_Instances>(u => u.InstanceID == user.InstanceID)[0];
string hash;
string salt;
new SaltedHash().GetHashAndSaltString(user.Password, out hash, out salt);
userRepo.CreateUserAuth(new UserAuth
{
Id = user.ServiceUserID,
UserName = user.UserName,
PasswordHash = hash,
Salt = salt,
RefIdStr = instance.InstanceConnectionString
}, user.Password);
}
}
catch (Exception ex)
{
throw ex;
}
}
}
CustomAuthUserSession.cs
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.Data;
using ServiceStack.Logging;
using ServiceStack.OrmLite;
using ServiceStack.OrmLite.SqlServer;
using ServiceStack.Text;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
namespace GrowerLiveAPI.App
{
public class CustomAuthUserSession : AuthUserSession
{
public string ConnectionString { get; set; }
public Funq.Container container;
public CustomAuthUserSession(Funq.Container container)
{
this.container = container;
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
{
base.OnAuthenticated(authService, session, tokens, authInfo);
var service = authService.ResolveService<IUserAuthRepository>();
var userAuth = service.GetUserAuth(session, tokens);
ConnectionString = userAuth.RefIdStr;
authService.SaveSession(this, SessionFeature.DefaultSessionExpiry);
var log = LogManager.GetLogger(GetType());
log.Info("Authenticated to webservice using: {0}".Fmt(JsonSerializer.SerializeToString(session.UserAuthName)));
HandleConnection();
}
private void HandleConnection()
{
ModifyConnectionString();
var log = LogManager.GetLogger(GetType());
log.Info("Connected to database using: {0}".Fmt(JsonSerializer.SerializeToString(ConnectionString)));
var dbFactory = new OrmLiteConnectionFactory(ConnectionString, SqlServerOrmLiteDialectProvider.Instance);
container.Register<IDbConnectionFactory>(dbFactory);
}
private void ModifyConnectionString()
{
string mode = ConfigurationManager.AppSettings["DevelopmentMode"].ToUpper();
if (mode == "DEVELOPMENT")
{
ConnectionString = ConnectionString.Replace("localaddress", "remoteaddress");
}
else if (mode == "STAGING")
{
ConnectionString = ConfigurationManager.AppSettings["StagingConnectionString"];
}
}
}
}
来自示例服务的一些代码
public class tbl_OrdersDataRepo
{
public IDbConnectionFactory dbFactory { get; set; }
public object PostOrder(tbl_Orders order)
{
using (IDbConnection db = dbFactory.OpenDbConnection())
{
try
{
if (IsInsertRequest(order))
{
return InsertOrder(db, order);
}
else
{
return UpdateOrder(db, order);
}
}
catch (Exception ex)
{
return new Exception(ex.Message);
}
}
}
private Boolean IsInsertRequest(tbl_Orders order)
{
if (order.OrderID > 0)
{
return false;
}
else
{
return true;
}
}
private int InsertOrder(IDbConnection db, tbl_Orders order)
{
if (order.OrderExternalID > 0)
{
tbl_Orders orderWithMappedIDs = mapExternalIDsToInternalIDs(db, order);
return (int)db.Insert(orderWithMappedIDs, selectIdentity: true);
}
else
{
return (int)db.Insert(order, selectIdentity: true);
}
}
private int UpdateOrder(IDbConnection db, tbl_Orders order)
{
tbl_Orders updatedOrder;
if (order.OrderExternalID > 0)
{
tbl_Orders orderWithMappedIDs = mapExternalIDsToInternalIDs(db, order);
tbl_Orders internalOrder = getOrder(db, orderWithMappedIDs.OrderID);
updatedOrder = getUpdatedOrder(internalOrder, orderWithMappedIDs);
}
else
{
tbl_Orders internalOrder = getOrder(db, order.OrderID);
updatedOrder = getUpdatedOrder(internalOrder, order);
}
db.Update(updatedOrder);
return updatedOrder.OrderID;
}
private tbl_Orders mapExternalIDsToInternalIDs(IDbConnection db, tbl_Orders externalOrder)
{
tbl_Orders internalOrder = externalOrder;
if (externalOrder.CustomerLocationID > 0)
internalOrder.CustomerLocationID = getInternalCustomerLocationID(db, externalOrder.CustomerLocationID);
if (externalOrder.NurseryLocationID > 0)
internalOrder.NurseryLocationID = getInternalNurseryLocationID(db, externalOrder.NurseryLocationID);
if (externalOrder.UserID > 0)
internalOrder.UserID = getInternalUserID(db, "harmony");
if (externalOrder.OrderTypeID > 0)
internalOrder.OrderTypeID = getInternalOrderTypeID(db, externalOrder.OrderTypeID);
if (externalOrder.EDIStatusID > 0)
internalOrder.EDIStatusID = getInternalEDIStatusID(db, externalOrder.EDIStatusID);
if (externalOrder.OrderDeliveryTimeWindowID > 0)
internalOrder.OrderDeliveryTimeWindowID = getInternalOrderDeliveryTimeWindowID(db, externalOrder.OrderDeliveryTimeWindowID);
return internalOrder;
}
private int getInternalCustomerLocationID(IDbConnection db, int externalCustomerLocationID)
{
var log = LogManager.GetLogger(GetType());
log.Info("Searching for an ExternalCustomerLocationID: {0}".Fmt(JsonSerializer.SerializeToString(externalCustomerLocationID)));
log.Info("In the database: {0}".Fmt(JsonSerializer.SerializeToString(db.Database)));
List<tbl_CustomerLocations> internalCustomers = db.Select<tbl_CustomerLocations>(c => c.CustomerLocationExternalID == externalCustomerLocationID);
if (internalCustomers.Count > 0)
{
tbl_CustomerLocations internalCustomer = internalCustomers[0];
return internalCustomer.CustomerLocationID;
}
else
{
throw new Exception("Cannot find a customer location with an id of '" + externalCustomerLocationID + "' in the remote database. " +
"Please make sure that the customer location exists in both databases, and that its external id is equal to " + externalCustomerLocationID + ".");
}
}
对于这篇文章的篇幅感到抱歉,但我认为太多的信息比太少了。谢谢你的帮助!
答案 0 :(得分:1)
ServiceStack的IOC和主机配置应该在AppHost.Configure()
内配置并在之后保持不变,即IOC依赖性永远不应该在运行时修改。
如果您在会话中存储客户连接字符串,则可以为用户特定数据库打开连接,例如:
public class MyServices : Service
{
public object Any(Request request)
{
var session = base.SessionAs<CustomAuthUserSession>();
using (var db = TryResolve<IDbConnectionFactory>()
.OpenDbConnectionString(session.ConnectionString))
{
//..
}
}
}
另一种方法是将连接字符串存储在RequestContext and use a custom IDbConnectionFactory。
中