一次只有一个用户使用整个 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的主要内容,如果未能解决你的问题,请参考以下文章