Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net mvc 如果用户从IdentityServer4中的其他浏览器/设备登录,如何检测并从应用程序注销?_Asp.net Mvc_Single Sign On_Identityserver4_Logout_Cookie Authentication - Fatal编程技术网

Asp.net mvc 如果用户从IdentityServer4中的其他浏览器/设备登录,如何检测并从应用程序注销?

Asp.net mvc 如果用户从IdentityServer4中的其他浏览器/设备登录,如何检测并从应用程序注销?,asp.net-mvc,single-sign-on,identityserver4,logout,cookie-authentication,Asp.net Mvc,Single Sign On,Identityserver4,Logout,Cookie Authentication,我已经在我的应用程序中实现了IdentityServer4 SSO。SSO可以很好地为所有客户端注销,但是有一个新的要求,即如果用户已经登录到应用程序,并且如果他尝试再次登录(从不同的设备/浏览器),那么他应该自动从以前的浏览器注销。我不明白这一点。如何实现这一点,如果有可能跟踪用户登录会话 更新:- 我们尝试了以下方法,我们使用“Action”过滤器属性将会话信息添加到全局静态变量中。在这里,我们在用户登录后存储登录会话信息 private class LoginSession

我已经在我的应用程序中实现了IdentityServer4 SSO。SSO可以很好地为所有客户端注销,但是有一个新的要求,即如果用户已经登录到应用程序,并且如果他尝试再次登录(从不同的设备/浏览器),那么他应该自动从以前的浏览器注销。我不明白这一点。如何实现这一点,如果有可能跟踪用户登录会话

更新:-

我们尝试了以下方法,我们使用“Action”过滤器属性将会话信息添加到全局静态变量中。在这里,我们在用户登录后存储登录会话信息

      private class LoginSession
        {
            internal string UserId { get; set; }
            internal string SessionId { get; set; }
            internal string AuthTime { get; set; }
            internal DateTimeOffset AuthDateTime
            {
                get
                {
                    if (!string.IsNullOrEmpty(AuthTime))
                        return DateTimeOffset.FromUnixTimeSeconds(long.Parse(AuthTime));
                    else
                        return DateTimeOffset.UtcNow;
                }
            }
        }

        private static List<LoginSession> LoginSessions = new List<LoginSession>();
私有类登录会话
{
内部字符串UserId{get;set;}
内部字符串SessionId{get;set;}
内部字符串AuthTime{get;set;}
内部日期时间偏移量AuthDateTime
{
得到
{
如果(!string.IsNullOrEmpty(AuthTime))
return DateTimeOffset.FromUnixTimeSeconds(long.Parse(AuthTime));
其他的
返回日期timeoffset.UtcNow;
}
}
}
私有静态列表登录会话=新列表();
在“ActionFilter”方法中,我们检查用户的会话id是否已经存在。如果会话存在,并且它的SessionId与声明会话id不匹配,那么我们将检查会话的登录时间。如果登录时间小于当前登录时间,则用户将退出系统,否则我们将使用最新的会话id和登录时间更新登录会话。由于第二次登录的工作流,登录会话将被更新,因为登录时间始终大于保存的登录会话信息。对于旧的登录会话,用户将从系统中注销,因为登录时间始终小于更新的会话信息

public class SessionValidationAttribute : ActionFilterAttribute
{        
    public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        string action = context.RouteData.Values["action"].ToString();

        if (!string.IsNullOrEmpty(action) &&
            context.Controller.GetType().GetMethod(action).GetCustomAttributes(typeof(AllowAnonymousAttribute), true).Length == 0)
        {
            var claims = ((ClaimsIdentity)((Microsoft.AspNetCore.Mvc.ControllerBase)context.Controller).User.Identity).Claims;

            var sessionId = claims.Where(x => x.Type == "sid").First().Value; // context.HttpContext.Request.Cookies.TryGetValue("idsrv.session", out var sessionId);
            var userId = claims.Where(x => x.Type == "sub").First().Value;
            var authTime = claims.Where(x => x.Type == "auth_time").First().Value;
            var authDateTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(authTime));

            if (LoginSessions.Where(x => x.UserId.Contains(userId)).Count() > 0) // if already logged in 
            {
                var latestLogin = LoginSessions.Where(x => x.UserId == userId).OrderByDescending(x => x.AuthDateTime).First();

                if (sessionId != latestLogin.SessionId)
                {
                   if(authDateTime > latestLogin.AuthDateTime) // login using new browser(session)
                   {
                       latestLogin.SessionId = sessionId; // assign latest sessionId
                       latestLogin.AuthTime = authTime; // assign latest authTime
                   }
                   else if (authDateTime < latestLogin.AuthDateTime) // login using old browser(session)
                   {
                     LoginSessions.RemoveAll(x => x.UserId == userId && x.SessionId!=latestLogin.SessionId);

                    context.Result = ((Microsoft.AspNetCore.Mvc.ControllerBase)context.Controller)
                                            .RedirectToAction(actionName: "Logout", controllerName: "Home",
                                            routeValues: new { tenant = string.Empty, isRemoteError = false });
                   }
                }
            }
            else
            {
                var newLogin = new LoginSession() { UserId = userId, SessionId = sessionId, AuthTime = authTime };
                LoginSessions.Add(newLogin);
            }
        }
        return base.OnActionExecutionAsync(context, next);
    }
}
公共类SessionValidationAttribute:ActionFilterAttribute
{        
公共重写任务OnActionExecutionAsync(ActionExecutionContext上下文,ActionExecutionDelegate下一步)
{
string action=context.RouteData.Values[“action”].ToString();
如果(!string.IsNullOrEmpty)(操作)&&
context.Controller.GetType().GetMethod(action).GetCustomAttributes(typeof(AllowAnonymousAttribute),true).长度==0
{
var声明=((ClaimsIdentity)((Microsoft.AspNetCore.Mvc.ControllerBase)context.Controller.User.Identity)声明;
var sessionId=claims.Where(x=>x.Type==“sid”).First().Value;//context.HttpContext.Request.Cookies.TryGetValue(“idsrv.session”,out var sessionId);
var userId=claims.Where(x=>x.Type==“sub”).First().Value;
var authTime=claims.Where(x=>x.Type==“auth_time”).First()值;
var authDateTime=DateTimeOffset.FromUnixTimeSeconds(long.Parse(authTime));
if(LoginSessions.Where(x=>x.UserId.Contains(UserId)).Count()>0)//如果已经登录
{
var latestLogin=LoginSessions.Where(x=>x.UserId==UserId).OrderByDescending(x=>x.AuthDateTime).First();
if(sessionId!=latestLogin.sessionId)
{
if(authDateTime>latestLogin.authDateTime)//使用新浏览器登录(会话)
{
latestLogin.SessionId=SessionId;//分配最新的SessionId
latestLogin.AuthTime=AuthTime;//分配最新的AuthTime
}
else if(authDateTimex.UserId==UserId&&x.SessionId!=latestLogin.SessionId);
context.Result=((Microsoft.AspNetCore.Mvc.ControllerBase)context.Controller)
.RedirectToAction(操作名称:“注销”,控制器名称:“主页”,
RouteValue:new{tenant=string.Empty,isRemoteError=false});
}
}
}
其他的
{
var newLogin=newloginsession(){UserId=UserId,SessionId=SessionId,AuthTime=AuthTime};
LoginSessions.Add(newLogin);
}
}
返回base.OnActionExecutionAsync(上下文,下一步);
}
}
我们对少数用户进行了测试,但该解决方案在有数千名用户登录到系统的实际场景中有效吗?全局使用静态变量存储会话信息是一个好主意吗?使用此功能的潜在缺点是什么?请给出建议。我们也乐于接受新的想法,如果有任何实现此功能的新方法,请告知我们


谢谢

免责声明:我没有使用IS4的实际经验

您可能有一个很好的理由,但我无法理解为什么在验证当前最新登录时覆盖
latestLogin
会话的详细信息

如果我没有弄错的话,这一行将在应用程序中的所有会话中循环,在接下来的几行中有多个类似的会话

if (LoginSessions.Where(x => x.UserId.Contains(userId)).Count() > 0)
这确实是您不希望在希望扩展的应用程序中执行的操作

不幸的是,我不熟悉IS4,我不能告诉你是否有可能完全通过使用它的API来解决这个问题,相反,我可以给你一些实用的建议

您可以使用单独的集中式存储,这种可能性是无限的,但类似的东西是完美的。那么算法就相当简单了:

  • 每当用户尝试登录时,检索存储在下的值