It's a common best practice to renew the HTTP session when logging in a user. This will force a new session ID, avoiding session fixation vulnerabilities.
Is there a preferred pattern for implementing this with CDI when @SessionScoped beans are involved? The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.
For example, assume a session bean for storing user login information:
@Named("sessionbean")
@SessionScoped
public class SessionBean implements Serializable {
private int userId;
private String username;
private List<String> privileges;
// Accessors omitted
}
And another bean for managing the login:
@Named("loginbean")
@ViewScoped
public class LoginBean implements Serializable {
private String username;
private String password;
@Inject private SessionBean session;
@Inject private SessionManager sessionManager;
@Inject private PrivilegeManager privilegeManager;
public String doLogin() {
String destinationUrl;
if (validate(username, password)) {
FacesContext context = FacesContext.getCurrentInstance();
// force renewal of HTTP session
context.getExternalContext().invalidateSession();
// retrieve new session bean ** No longer works with CDI **
Application app = context.getApplication();
session = app.evaluateExpressionGet(context, "#{sessionbean}", SessionBean.class);
session.setUsername(username);
session.setSessionId(sessionManager.createNewSession(username));
session.setPrivileges(privilegeManager.getPrivileges(username));
destinationUrl = createLandingPageUrl();
} else {
destinationUrl = createFailureUrl("Unknown user or password");
}
return destinationUrl;
}
}
With Managed Beans this would retrieve a new SessionBean, but with CDI, the code above would just return the same SessionBean. Any recommendations or clever ideas?
The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.
Then don't invalidate the session, but change the session ID. In other words, don't use HttpSession#invalidate()
, but use HttpServletRequest#changeSessionId()
(new since Servlet 3.1, which you should undoubtedly already be using given that you're using JSF 2.3).
In code, replace
// force renewal of HTTP session object
context.getExternalContext().invalidateSession();
by
// force renewal of HTTP session ID
((HttpServletRequest) context.getExternalContext().getRequest()).changeSessionId();
This basically changes the JSESSIONID
cookie without changing the HttpSession
. It's perfect for session fixation prevention.
Explicitly invalidating the session is usually only useful during logout.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With