一次只有一个用户使用整个 Web 应用程序 - Spring Boot

Posted

技术标签:

【中文标题】一次只有一个用户使用整个 Web 应用程序 - Spring Boot【英文标题】:Only one User using entire web application at a time - Spring boot 【发布时间】:2021-01-30 02:09:33 【问题描述】:

在 Spring Boot 应用程序中,一次只能有一个用户使用某个页面(我们称之为home.jsp)。如果其他用户在访问相同的 url 时出现,则应将其重定向到不同的页面(我们称之为another_home.jsp)。用户没有登录,只是按原样使用应用程序。 home.jsp 可以使用任何策略,可以是先到先服务或任何其他策略。

如果有多个用户同时使用应用程序,则只有一个用户应使用 home.html,其余用户应使用 another_home.jsp

由于应用程序不需要登录,我相信我需要匿名会话。此外,会话需要在一段时间不活动后过期。我已经搜索了 spring security 但找不到任何东西。

【问题讨论】:

你想怎么弄清楚,第一个用户还在页面上?下一个应用程序什么时候可以使用? Spring 安全性维护有关匿名会话的信息。可能是某种类型的机制已经在某些库中可用,或者在我的上下文中可以通过一些配置调整来重现。 【参考方案1】:

我认为你甚至不需要弹簧安全。简单的 http 会话也可以。据我所知,您只想将流分配给一个用户,为此您需要第一个用户的会话 ID,您可以在请求再次出现时与之进行比较。所以存储会话 id 并在一些 Time 对象或 Date 对象超时后过期。

在属性中

server.servlet.session.timeout = 600   // 10 minutes

类似的东西

private String currSessionId = null;
private Date lastDate = new Date();
private Integer TIMEOUT = 600000;    // 10 minutes

public String loadHomePage(Model model) 
    if(currSessionId!=null && new Date().getTime()- lastDate.getTime()>TIMEOUT)
        currSessionId = null;
    
    if(currSessionId==null)
        currSessionId = session.getId();
        lastDate = new Date();
        return "home";
    else
        if(session.getId().equals(currSessionId))
            return "home";
        else
            return "another_home";
        
    

当您没有登录用户来管理并且不需要记住用户上次离开的状态时,这就像它一样简单。如果有帮助,请告诉我。

【讨论】:

看起来超级简单。让我试一试。【参考方案2】:

所以...首先,这听起来是个坏主意。我很好奇你为什么需要这种不寻常的行为。可能有更明智的方法。

正如 Gregor 所说,重定向代码部分相当简单:

if(pageLock.getUser() == null) 
  pageLock.setUser(user);
 
if(user.equals(pageLock.getUser())) 
  return "home.jsp"
 else 
  return "redirect:/another_home.jsp"


实际上更棘手的是“过期”锁的部分。用户很可能会简单地关闭浏览器而不单击“注销”(或其他),从而永远保持锁定状态。在另一个极端情况下,用户可能会去吃午饭,但其浏览器仍会打开页面数小时。

所以这是您要添加的第一件事:页面上的一些保持活动机制,定期延长锁定,以及一些过期检查器,如果一段时间内没有收到任何内容,则释放锁定。


...但就像我一开始说的那样,整件事听起来很可疑。

【讨论】:

我使用的 API 一次只允许一个流连接。因此,我无法将流提供给多个用户。所以基本上我想做的是在流媒体可用时为第一个用户提供服务,并在用户离开或超时(可能是 15 分钟)时释放。我确实认为保持活动机制是一个好主意。让我试试@Gregor Riegler 提供的春季安全会话链接 我在home.jsp 上传输数据,所以当流打开时,即使没有触发其他操作,它也不应该释放资源。保持活动机制是可行的,但我没有花太多时间从头开始构建机制,因为它不是优先事项。那么您知道任何用于您建议的基本实现的库吗?【参考方案3】:

您需要创建一个服务器端状态,该状态要么为空,要么存储当前声明/home.jsp 的访问者的标识符。 这可以是单例 Bean 上的字段,也可以是数据库中的实体。 它必须自动过期,否则它将永远阻止新访问者提出索赔。 只要状态为空,第一个访问者标识符就会存储在该状态中。 从那一刻起,您会将所有其他访问者重定向到another_home.jsp

所以控制器代码应该是这样的

if(visitorHoldsTheClaim()) 
  return "home.jsp"
 else if (noClaimActive()) 
  createClaimForVisitor();
  return "home.jsp"
 else 
  return "redirect:/another_home.jsp"

根据您的实现,这些方法会做不同的事情。

我通常建议不要使用服务器端会话状态(更多信息请参见 Roy Fieldings Dissertation), 但是对于您的用例,您需要一种方法来识别多个请求的访问者。 会话肯定是实现这一目标的一种非常简单的方法。 您至少可以通过一次只创建一个会话来最小化会话使用 - 为持有声明的访问者创建的会话。 在这种情况下,您永远不会有超过一个开放会话,拥有该会话的访问者就是持有声明的访问者。

所以在这种情况下,实现将是这样的:

if(currentUserHasASession())  // checks if the current user has a session, but !!!does not create a new session if it does not exist!!! careful, HttpServletRequest.getSession(true) would create it!
  return "home.jsp"
 else if (serverHasNoSessions())  // https://***.com/questions/49539076/how-can-i-get-a-list-of-all-sessions-in-spring
  createSessionForUser(); // HttpServletRequest.getSession(true)
  return "home.jsp"
 else 
  return "redirect:/another_home.jsp"

请记住,这仅在您不在其他地方创建会话时才有效。 所以你必须配置 Spring Boot/Spring Security 以不创建会话。 How to make spring boot never issue session cookie?

还要记住并发性。例如,如果您只有一个服务器实例,您可以将此代码放入 synchronized 方法中,以避免两个访问者同时创建声明。

【讨论】:

抱歉,您提供的链接没有多大用处。 synchronized 是一个解决方案,但它会使应用程序变得非常缓慢,也不是我想要做的。

以上是关于一次只有一个用户使用整个 Web 应用程序 - Spring Boot的主要内容,如果未能解决你的问题,请参考以下文章

Bean作用域

应用服务器集群的session管理

ServletConfig对象和ServletContext对象

浅谈web请求过程

vue路由

Vue路由