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印象不佳。

 

posted @ 2021-03-19 00:12  平静寄居者  阅读(127)  评论(0编辑  收藏  举报