Salesforce限制并发用户的实现
前几天有个需求,对于特定的profile,同一时间只允许一台设备登陆。查了资料,用LoginFlow结合apex实现,Salesforce官网上有个帖子,详细解释了实现方法。(https://developer.salesforce.com/docs/atlas.en-us.securityImplGuide.meta/securityImplGuide/security_login_flow_limit_concurrent_sessions.htm)但代码很古老,完全过时了,不过,取其精华就可以了:
public without sharing class LimitConcurrentSessions { @InvocableMethod public static List<Integer> LimitLoginSession() { Integer no = 0; List<AuthSession> sessions; String userid = UserInfo.getUserId(); sessions = [Select Id, ParentId, SessionType from AuthSession where UsersId=:userid]; for (AuthSession s : sessions) { // Count only parent and non-temp and non-internal sessions if(s.ParentId == null && s.SessionType != 'TempUIFrontdoor' && s.SessionType != 'InternalServiceCall' && s.SessionType != 'TempChatterNetworks') { no++; } } List<Integer> result = new List<Integer>(); result.add(no); return result; } }
然后在Flow中调用,若no >1,则将变量LoginFlow_ForceLogout设成True,强行退出。试了下,运行正常。
本来以为解决了,没想到测试员报告说尽管全退出了,但登陆时仍然提示已登陆了一台设备,强行退出。查了下AuthSession对象,果然有两个Session在那里。不知什么原因退出时session没有清掉,于是干脆写个trigger:
trigger LogoutEventTrigger on LogoutEventStream (after insert) { LogoutEventStream event = Trigger.new[0]; List<Profile> profiled = [SELECT id, Name FROM Profile WHERE Name = :'foo']; if (profiled.size() > 0) { Id profileId = profiled[0].id; Id userId = event.UserId; List<User> loggedOut = [SELECT ProfileId FROM User WHERE id = :userId]; if (loggedOut.size() > 0 && loggedOut[0].ProfileId == profileId) { List<AuthSession> sessions = [SELECT Id, usersid FROM AuthSession WHERE usersid = :userId]; if (sessions.size() > 0) { delete(sessions); } } } }
然后在Setup - Event Manager - Logout Event - Enable Streaming,使trigger生效。这下消停了一会,又有人报错,说有个账户在第一次登陆(由管理员发邮件,点击邮件中的链接登陆)时,提示已登陆一台设备,强行退出。查AuthSession对象,发现每次点链接时都会出现两个session!于是利用AuthSession对象的LoginHistory域,改写代码:
public without sharing class LimitConcurrentSessions { @InvocableMethod public static List<Integer> LimitLoginSession() { Integer no = 0; List<AuthSession> sessions; String userid = UserInfo.getUserId(); List<Datetime> loginTime = new List<DateTime>(); sessions = [Select Id, ParentId, SessionType, LoginHistoryId from AuthSession where UsersId=:userid]; for (AuthSession s : sessions) { // Count only parent and non-temp and non-internal sessions if(s.ParentId == null && s.SessionType != 'TempUIFrontdoor' && s.SessionType != 'InternalServiceCall' && s.SessionType != 'TempChatterNetworks') { List<LoginHistory> history = [SELECT LoginTime FROM LoginHistory WHERE Id = :s.LoginHistoryId]; if (history.size() > 0) { if (!loginTime.contains(history[0].LoginTime)) { loginTime.add(history[0].LoginTime); no++; } } } } List<Integer> result = new List<Integer>(); result.add(no); return result; } }
也就是如果两个session的时间完全一样,就忽略。这以后就正常了,没有人报错。
上面多次用了List保存SOQL的结果,目的是为了避免万一找不到记录,报Exception。
这个功能的实现又一次使我对Salesforce印象不佳。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律