我有一个 home.jsf
,它调用一个登录 servlet,该 servlet 会根据用户名和密码查看数据库并查询出 user
对象。然后,我将该 user
对象保存到属性名称 user
下的 session 中,如下所示 request.getSession().setAttribute("user", user);
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
boolean remember = "true".equals(request.getParameter("remember"));
//Hashing the password with SHA-256 algorithms
password = hash(password);
HttpSession s = request.getSession(false);
if (s != null) {
logger.log(Level.INFO, "Id: {0}", s.getId());
}
User user = scholarEJB.findUserByUserNamePassword(username, password);
try {
if (user != null) {
request.login(username, password);
request.getSession().setAttribute("user", user);
if (remember) {
String uuid = UUID.randomUUID().toString();
UserCookie uc = new UserCookie(uuid, user.getId());
scholarEJB.persist(uc);
Helper.addCookie(response, Helper.COOKIE_NAME, uuid, Helper.COOKIE_AGE);
}else{
//If the user decide they dont want us to remember them
//anymore, delete any cookie associate with this user off
//the table
scholarEJB.deleteUserCookie(user.getId());
Helper.removeCookie(response, Helper.COOKIE_NAME);
}
response.sendRedirect("CentralFeed.jsf");
}else{
response.sendRedirect("LoginError.jsf");
}
} catch (Exception e) {
response.sendRedirect("LoginError.jsf");
}
然后我有一个映射到所有安全页面的文件管理器,它将尝试从 session 中检索用户对象,否则,将我重定向到 home.jsf
以再次登录
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession s = request.getSession(false);
if (s != null) {
logger.log(Level.INFO, "Id Before: {0}", s.getId());
}
User user = (User) request.getSession().getAttribute("user");
s = request.getSession(false);
if (s != null) {
logger.log(Level.INFO, "Id After: {0}", s.getId());
}
if (user == null) {
String uuid = Helper.getCookieValue(request, Helper.COOKIE_NAME);
if (uuid != null) {
user = scholarEJB.findUserByUUID(uuid);
if (user != null) {
request.getSession().setAttribute("user", user); //Login
Helper.addCookie(response, Helper.COOKIE_NAME, uuid, Helper.COOKIE_AGE);
} else {
Helper.removeCookie(response, Helper.COOKIE_NAME);
}
}
}
if (user == null) {
response.sendRedirect("home.jsf");
} else {
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
chain.doFilter(req, res);
}
现在,正如您在这里看到的,我也操纵了一些 Cookie,但这仅在我选中记住我
时才会发生。现在我位于 CentralFeed.jsf
中,但从这里发送的任何请求都会返回到 home.jsf
以便再次登录。我通过调试器,所以当我第一次登录时,第一次进入过滤器时,我通过 request.getSession().getAttribute("user"从 session 中成功检索了
。但之后,当我返回过滤器时,我不再使用 session 属性user
对象”);user
。我在 web.xml 中将 session 超时设置为 30 分钟
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
编辑
现在,当我打印出请求之间的 session ID 时,它实际上是不同的 session ID,但我不知道为什么?请帮忙。
编辑2
@BalusC:我实际上确实使 session 无效。当时,您向我展示了当用户在其他地方登录时如何强制注销(http://stackoverflow.com/questions/2372311/jsf-how-to-invalidate-an-user-session-when-he-logs-两次使用相同的凭证)。所以在 User 实体里面我有这个
@Entity
public class User implements Serializable, HttpSessionBindingListener {
@Transient
private static Map<User, HttpSession> logins = new HashMap<User, HttpSession>();
@Override
public void valueBound(HttpSessionBindingEvent event) {
HttpSession session = logins.remove(this);
if (session != null) {
session.invalidate(); //This is where I invalidate the session
}
logins.put(this, event.getSession());
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
logins.remove(this);
}
}
在 valueBound
方法中,我确实使 session 无效,当我将其注释掉时,一切正常。我浏览了调试器,发生的情况如下。当我第一次登录时,LoginServlet 捕获它。然后 request.getSession().setAttribute("user", user);
行调用 valueBound
方法。然后 Filter 被调用,chain.doFilter(req, res);
行再次调用 valueBound
方法,这一次,session
是不为 null,因此它进入 if 和 session.invalidate
。我评论了 session.invalidate ,它起作用了。但正如您可能已经猜到的那样,当用户在其他地方登录时我无法强制注销。您是否看到此 BalusC 的明显解决方案?
最佳答案
HTTP session 由 JSESSIONID
cookie 维护。确保您的 Helper.COOKIE_NAME
不使用相同的 Cookie 名称,否则它将覆盖 session Cookie。
如果不是这样的话,那我就不知道了。我将使用 Firebug 来调试 HTTP 请求/响应 header 。在全新 session 的第一个 HTTP 响应中,您应该会看到 Set-Cookie
header 以及带有 session ID 的 JSESSIONID
cookie。在同一 session 中的所有后续请求中,您应该会看到 Cookie
header 以及带有 session ID 的 JSESSIONID
cookie。
当 Cookie
header 不存在或包含 JSESSIONID
cookie 时,将创建一个新 session (对于服务器端)不存在 session ID(因为它是已失效),或者当服务器使用具有不同 session ID 的新 Set-Cookie
header 进行响应时。这应该可以帮助您找出罪魁祸首。是服务器生成了新的 session cookie吗?或者是客户端没有发回 session cookie?
如果是服务器,则在服务器端的某个位置 session 已过期/无效。尝试在 HttpSession#invalidate()
上放置一个断点以进一步确定它。
如果是客户端(这会很奇怪,因为它似乎很好地支持 cookies),那么尝试对重定向 URL 进行编码以包含 JSESSIONID
。
response.sendRedirect(response.encodeRedirectURL(url));
如有必要,请尝试使用不同的客户端以排除其中一个或另一个。
关于JavaEE + Glassfishv3.0.1 : Losing session attribute (SessionId is not the same between request),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5156942/