Let the following class be a session scoped CDI managed bean.
@Named
@SessionScoped
public class SessionUtils implements Serializable
{
private Map<String, Object>sessionMap;
private static final long serialVersionUID=1l;
public SessionUtils() {}
@PostConstruct
private void init() {
sessionMap=new HashMap<String, Object>();
}
public Map<String, Object> getSessionMap() {
return sessionMap;
}
}
The map is used to store user specific information.
I am using JAAS for authentication. There is a login Filter
. Its skeleton looks like the following.
@WebFilter(filterName = "SecurityCheck", urlPatterns = {"/WEB-INF/jaas/*"}, dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD})
public final class SecurityCheck implements Filter
{
@Inject
private UserBeanLocal userService;
@Inject
private SessionUtils sessionUtils;
public SecurityCheck() {}
private void doBeforeProcessing(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String userName = request.getParameter("userName");
request.login(userName != null ? userName.trim() : "", request.getParameter("password"));
}
private void doAfterProcessing(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
Map<String, Object> sessionMap = sessionUtils.getSessionMap();
if (request.isUserInRole("ROLE_ADMIN")) {
UserTable userTable = userService.setLastLogin(request.getParameter("userName"));
userTable.setPassword(null);
//sessionMap.put("user", userTable);
request.getSession().setAttribute("newUser", new User());
response.sendRedirect(request.getContextPath() + "/admin_side/Home.xhtml");
}
//else {Repeat for other authorities}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String userName = httpServletRequest.getParameter("userName");
HttpServletResponse httpServletResponse = ((HttpServletResponse) response);
try {
doBeforeProcessing(httpServletRequest, httpServletResponse);
} catch (ServletException e) {
httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/utility/LoginError.xhtml");
return;
}
chain.doFilter(request, response);
doAfterProcessing(httpServletRequest, httpServletResponse);
}
}
I need to invoke the following HttpSessionBindingListener
public final class User implements HttpSessionBindingListener {
private static final Map<UserTable, HttpSession> logins = new HashMap<UserTable, HttpSession>();
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("valueBound() called.");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("valueUnbound() called.");
}
}
The overridden methods are only invoked, when using the code along the following line.
request.getSession().setAttribute("newUser", new User()); //Or remove
But they are not invoked as obvious, if an instance of User
is simply stored into the session map (in that CDI bean).
sessionMap.put("newUser", new User());
Are there other ways round in JAAS/CDI to deal with the same - to simulate HttpSessionBindingListener
?
What I want to do is : if a user forgot to log out, the (previous still active) session should be removed in his/her next attempt to login.
One additional thing : UserTable
(not User
- it is just an example.) is an actual JPA entity class. The HttpSessionBindingListener
needs to be implemented on that JPA entity which in turn requires an additional dependency on the service layer from the javax.servlet
package unnecessarily increasing coupling between modules.
Can this be isolated so that the HttpSessionBindingListener
can be implemented on the web layer (regardless of that JPA entity class - UserTable
and still serving for that class i.e when an instance of UserTable
is put into the session, the valueBound()
method is called... and valueUnbound()
, when an instance of UserTable
is removed from HttpSession
, replaced by another session attribute or the session itself is destroyed/invalidated)? I expect some ways in advanced Java EE.
The question title is not so meaningful as it should be. I will edit it later, when I envision a more meaningful title or you may voluntarily edit it before that time, if you like.
As far as I understood, question is following, why
HttpSessionBindingListener
is called only when you callrequest.getSession()
?Session is became created only when you call
request.getSession()
and therefore listener is invoked. So you need to call that method in order to start a session, or it will be started if you call that later on during request.By the way, it is bad practice to store
HttpSession
in static variable, it may lead to memory leak, if you will "forget" to remove it.In order to achieve what you're trying to do, I would store only a static set of
request.getSession().getId()
in session listener. And in the filter, I would check if the set has suchid
, so you will get an existing session or create a new one. Or still store in map, if you really need to know to which user a session belongs.