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

Only one User using entire web application at a time - Spring boot

在 spring 引导应用程序中,一次只能有一个用户使用特定页面(我们称之为 home.jsp)。如果另一个用户在访问同一个 url 时出现,则他们应该被重定向到不同的页面(我们称之为 another_home.jsp)。用户不登录,只是按原样使用应用程序。 home.jsp 可以使用任何政策,可以是先到先得或任何其他政策。

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

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

您需要创建一个服务器端状态,该状态要么为空,要么存储当前声明 /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()) { // 
  createSessionForUser(); // HttpServletRequest.getSession(true)
  return "home.jsp"
} else {
  return "redirect:/another_home.jsp"
}

请记住,这只有在您不在其他地方创建会话时才有效。 因此,您必须配置 Spring Boot/Spring 安全性以不创建会话。 How to make spring boot never issue session cookie?

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

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

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

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

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

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


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

我认为你甚至不需要 spring 安全。简单的 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";
        }
    }
}

当您没有要管理的登录用户并且也不需要记住用户离开的先前状态时,这就很简单了。如果有帮助,请告诉我。