Session JavaEE6并发会话和共享
我正在使用Java EE 6 JSF CDI EJB开发一个web应用程序,该应用程序不允许同时登录同一用户和密码 我喜欢的是: 如果用户登录两次,则需要使第一个会话无效,并且旧会话数据(包括所有具有SessionScope或其他作用域(如来自Apache CODI的WindowsScope)的CDI bean)将传输到新会话Session JavaEE6并发会话和共享,session,jakarta-ee,cdi,sharing,codi,Session,Jakarta Ee,Cdi,Sharing,Codi,我正在使用Java EE 6 JSF CDI EJB开发一个web应用程序,该应用程序不允许同时登录同一用户和密码 我喜欢的是: 如果用户登录两次,则需要使第一个会话无效,并且旧会话数据(包括所有具有SessionScope或其他作用域(如来自Apache CODI的WindowsScope)的CDI bean)将传输到新会话 这有点像一种被通缉的会话劫持方法:-在JavaEE6中没有这种内在机制 由于我无法想象您想要将某个正在进行的用例(例如,从一个会话到另一个会话的开放签出过程)之类的东西转
这有点像一种被通缉的会话劫持方法:-在JavaEE6中没有这种内在机制 由于我无法想象您想要将某个正在进行的用例(例如,从一个会话到另一个会话的开放签出过程)之类的东西转移到另一个会话,因此我建议您只需跟踪用户的GUI状态 RESTful URL听起来是实现这一点的理想方法。保留最后一个用户URL/用户操作,例如www.myapp.com/orders/new/12,并在新登录时重新打开
如果您不想在数据库中持久化这一点,那么应用程序范围的映射userid/url可能是KISS方式。在JavaEE6中没有固有的机制 由于我无法想象您想要将某个正在进行的用例(例如,从一个会话到另一个会话的开放签出过程)之类的东西转移到另一个会话,因此我建议您只需跟踪用户的GUI状态 RESTful URL听起来是实现这一点的理想方法。保留最后一个用户URL/用户操作,例如www.myapp.com/orders/new/12,并在新登录时重新打开
如果您不想在DB中保持这种状态,可以使用应用程序范围的映射userid/url。您可以为用户使用无状态bean,因此每次尝试登录/重新登录时,当前会话都会在登录过程开始时失效 考虑这种方法:
try {
session = request.getSession(); //the request is passed by another page or action
if(session.getAttribute("user") != null) {
//your code to forward or handle the existing user (re-log in/ do nothing etc.)
}
您可以为用户使用无状态bean,因此每次尝试登录/重新登录时,当前会话都会在登录过程开始时失效 考虑这种方法:
try {
session = request.getSession(); //the request is passed by another page or action
if(session.getAttribute("user") != null) {
//your code to forward or handle the existing user (re-log in/ do nothing etc.)
}
我在过滤器的帮助下解决了这个问题 公共类SessionReplicationFilter实现筛选器{
@Inject
SessionReplicationManager manager;
public SessionReplicationFilter() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//Process chain first
if (chain != null) {
chain.doFilter(request, response);
}
//check http request
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// Retrieve the session and the principal (authenticated user)
// The principal name is actually the username
HttpSession session = httpRequest.getSession();
Principal principal = httpRequest.getUserPrincipal();
if (principal != null && principal.getName() != null && session != null) {
manager.checkExistingSession(principal.getName(), session))
}
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
经理看起来很像
@适用范围
公共类SessionReplicationManager{
private Map<String, HttpSession> map = new ConcurrentHashMap<String, HttpSession>();
public boolean checkExistingSession(String user, HttpSession session) {
if (map.keySet().contains(user)) {
if (!session.getId().equals(map.get(user).getId())) {
System.out.println("User already logged in ");
HttpSession oldSession = map.get(user);
// copies all attributes from the old session to the new session (replicate the session)
Enumeration<String> enumeration = oldSession.getAttributeNames();
while (enumeration.hasMoreElements()) {
String name = enumeration.nextElement();
System.out.println("Chaning attribut " + name);
session.setAttribute(name, oldSession.getAttribute(name));
}
// invalidates the old user session (this keeps one session per user)
oldSession.invalidate();
map.put(user, session);
return true;
}
} else {
System.out.println("Putting "+user+" into session cache");
map.put(user, session);
return false;
}
return false;
}
}
它可以很好地与CoDI ViewScope注释bean配合使用
如果第一个用户失效,每个AJAX请求都会导致会话过期异常,即使使用还原会话按钮也可以轻松处理该异常
viewscoped bean唯一的一个小问题是,它们获得了一个新的视图id。通过将它们更改回原始视图id,一切正常
我需要补充的是:
自动注销ajax轮询,WebSocket,。。。
存储所有ViewScope id的某种注册表
此评论中缺少:
web.xml配置
关于我在过滤器的帮助下解决了这个问题 公共类SessionReplicationFilter实现筛选器{
@Inject
SessionReplicationManager manager;
public SessionReplicationFilter() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//Process chain first
if (chain != null) {
chain.doFilter(request, response);
}
//check http request
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// Retrieve the session and the principal (authenticated user)
// The principal name is actually the username
HttpSession session = httpRequest.getSession();
Principal principal = httpRequest.getUserPrincipal();
if (principal != null && principal.getName() != null && session != null) {
manager.checkExistingSession(principal.getName(), session))
}
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
经理看起来很像
@适用范围
公共类SessionReplicationManager{
private Map<String, HttpSession> map = new ConcurrentHashMap<String, HttpSession>();
public boolean checkExistingSession(String user, HttpSession session) {
if (map.keySet().contains(user)) {
if (!session.getId().equals(map.get(user).getId())) {
System.out.println("User already logged in ");
HttpSession oldSession = map.get(user);
// copies all attributes from the old session to the new session (replicate the session)
Enumeration<String> enumeration = oldSession.getAttributeNames();
while (enumeration.hasMoreElements()) {
String name = enumeration.nextElement();
System.out.println("Chaning attribut " + name);
session.setAttribute(name, oldSession.getAttribute(name));
}
// invalidates the old user session (this keeps one session per user)
oldSession.invalidate();
map.put(user, session);
return true;
}
} else {
System.out.println("Putting "+user+" into session cache");
map.put(user, session);
return false;
}
return false;
}
}
它可以很好地与CoDI ViewScope注释bean配合使用
如果第一个用户失效,每个AJAX请求都会导致会话过期异常,即使使用还原会话按钮也可以轻松处理该异常
viewscoped bean唯一的一个小问题是,它们获得了一个新的视图id。通过将它们更改回原始视图id,一切正常
我需要补充的是:
自动注销ajax轮询,WebSocket,。。。
存储所有ViewScope id的某种注册表
此评论中缺少:
web.xml配置
关于您为什么不隐藏已登录用户的登录页面,并以您已登录的消息拒绝任何登录尝试。如果您希望以其他用户身份登录,请注销。或者像这样的明智之举。没那么简单。因为用户希望在两个不同的站点上工作,而不关心登录。他只想看到应用程序的相同输出和相同状态。那么为什么需要使第一个会话无效?第二个会话可能会更改第一个会话上的数据。图像您在第一个会话中打开了订单,然后您更改了工作区,并且您希望在第二个会话中再次编辑订单,但您忘记注销,此时未进行任何更改以保留任何数据。我不想使会话无效,只想将所有数据传输到新创建的oneMyFaces CODI Bean,它存储一个窗口和会话中的所有窗口。如果复制会话的内容,那么也会为所有窗口复制这些bean。为什么不隐藏已登录用户的登录页面,并以您已登录的消息拒绝任何登录尝试。如果您希望以其他用户身份登录,请注销。或者像这样的明智之举。没那么简单。因为用户希望在两个不同的站点上工作,而不关心登录。他只想看到相同的输出和
应用程序的相同状态为什么需要使第一个会话无效?第二个会话可能会更改第一个会话上的数据。图像您在第一个会话中打开了订单,然后您更改了工作区,并且您希望在第二个会话中再次编辑订单,但您忘记注销,此时未进行任何更改以保留任何数据。我不想使会话无效,只想将所有数据传输到新创建的oneMyFaces CODI Bean,它存储一个窗口和会话中的所有窗口。如果您复制会话的内容,那么您也可以为所有窗口复制这些bean。我不确定这是否适用于我的所有用例。但我会尝试一下。只是出于好奇——你正在建立什么样的系统?为什么用户会在相对较短的时间内登录到不同的站点?请想象一个具有不同终端的POS系统。我不能详述。对不起,我不确定这是否能在我所有的用例中都起作用。但我会尝试一下。只是出于好奇——你正在建立什么样的系统?为什么用户会在相对较短的时间内登录到不同的站点?请想象一个具有不同终端的POS系统。我不能详述。我很抱歉,我希望看到你回复的任何人都会意识到这只是一个疯狂的内存泄漏HttpSessions映射永远不会被清除。你是对的。此处缺少HttpSessionListener,它观察sessionDestroyed事件。侦听器从映射中删除所有无效会话,即超时、注销等。我希望看到您回复的任何人都会意识到这只是一个疯狂的内存泄漏HttpSessions映射永远不会被清除。您是对的。此处缺少HttpSessionListener,它观察sessionDestroyed事件。侦听器从映射中删除所有无效会话,即超时、注销等,。。。