Delphi(FireDAC)桌面应用登录后多用户会话差异化实现咨询
解决Delphi + FireDAC + MSSQL桌面应用多用户会话隔离问题
嘿,我来帮你搞定这个问题——你现在的核心痛点就是没有为每个登录用户建立独立的会话上下文,导致所有用户共享了同一套数据库连接、状态判断逻辑,自然看到的数据和能用的功能都一模一样。下面是针对桌面应用场景的具体落地方案,完全不需要分布式那套复杂架构:
1. 给每个用户分配专属的FireDAC连接
别再用全局共享的FDConnection了!这是串用数据的根源。你可以封装一个用户会话类,把用户信息和专属数据库连接绑定在一起:
type TUserSession = class public UserID: Integer; UserName: string; Permissions: TStringList; // 存用户的功能权限,比如['查看订单', '编辑客户'] DBConn: TFDConnection; constructor Create; destructor Destroy; override; end; constructor TUserSession.Create; begin inherited; DBConn := TFDConnection.Create(nil); // 配置MSSQL连接参数:服务器、数据库名这些根据你的实际情况填 DBConn.DriverName := 'MSSQL'; DBConn.Params.Values['Server'] := '你的SQL服务器地址'; DBConn.Params.Values['Database'] := '你的数据库名'; // 如果是业务层区分用户(共用一个数据库登录账号),这里就用通用账号密码;如果每个用户有独立SQL账号,就用用户输入的账号密码配置 DBConn.Connected := True; end; destructor TUserSession.Destroy; begin DBConn.Free; inherited; end;
登录成功后,实例化这个TUserSession,把它存在主窗体的属性里(或者一个全局变量,但如果支持多窗口登录,最好绑定到对应窗口),后续所有数据库操作都用这个专属的DBConn。
2. 数据库层面实现数据隔离
光有独立连接还不够,得确保每个用户只能看到自己的数据,两种常用方式:
方式一:业务SQL里加用户ID过滤
这是最直接的方式,所有查询都带上当前用户的ID作为条件:
// 假设当前会话存在MainForm.UserSession里 FDQuery.SQL.Text := 'SELECT * FROM 订单表 WHERE 创建人ID = :CurrentUserID'; FDQuery.ParamByName('CurrentUserID').AsInteger := MainForm.UserSession.UserID; FDQuery.Open;
方式二:用MSSQL行级安全(RLS)自动过滤
如果不想每个SQL都写过滤条件,可以在数据库端配置RLS,让数据库自动帮你过滤数据:
首先在SQL Server里创建会话上下文函数和安全策略:
-- 创建获取当前用户ID的函数 CREATE FUNCTION dbo.GetCurrentLoginUserID() RETURNS INT WITH SCHEMABINDING AS BEGIN RETURN CAST(SESSION_CONTEXT(N'LoginUserID') AS INT); END; -- 给订单表添加行级安全策略 CREATE SECURITY POLICY OrderDataFilterPolicy ADD FILTER PREDICATE dbo.GetCurrentLoginUserID() = 创建人ID ON dbo.订单表 WITH (STATE = ON);
然后在Delphi登录成功后,通过专属连接把当前用户ID写入数据库会话上下文:
MainForm.UserSession.DBConn.ExecSQL('EXEC sp_set_session_context N''LoginUserID'', :UserID', [MainForm.UserSession.UserID]);
这样之后,所有针对订单表的查询都会自动过滤,不用在业务代码里加条件,非常省心。
3. 功能权限的隔离控制
要让不同用户用不同功能,得做两步:
- 登录时从数据库加载用户的权限列表(比如从
用户权限表查询该用户拥有的权限),存入TUserSession.Permissions - 在界面和业务逻辑层做权限校验:
// 比如判断用户是否有编辑客户的权限 if MainForm.UserSession.Permissions.IndexOf('编辑客户') = -1 then begin btnEditCustomer.Enabled := False; ShowMessage('抱歉,你没有编辑客户的权限'); end;
注意:别只在界面层做控制!业务逻辑层也要加校验,防止用户通过直接调用方法等方式绕过界面限制。
4. 会话管理的关键注意点
- 用户注销时,一定要销毁对应的
TUserSession实例,关闭专属的FDConnection,释放资源,避免内存泄漏和连接占用 - 如果你的应用支持同时登录多个用户(比如开多个窗口对应不同账号),要确保每个窗口绑定自己的
TUserSession,别共用全局实例,可以把会话存在窗口的Tag属性或者自定义属性里 - 如果有后台线程处理数据库操作,要确保线程使用的是当前用户的专属连接,别用全局连接
避坑提醒
- 绝对不要共用全局FDConnection:哪怕你登录后切换数据库用户,也可能因为连接池或会话上下文残留导致数据串用
- 权限控制要多层校验:界面层、业务逻辑层、数据库层都要做,确保万无一失
- 用RLS的话,要注意连接断开重连后,需要重新设置会话上下文,否则过滤会失效
内容的提问来源于stack exchange,提问作者user9818764
相关产品推荐
相关产品推荐

