I am playing with JavaEE 7 and tried to write a webapp with a simple login mechanism.
There is an EJB entity class persisted with JPA called User
holding data about users. In the WAR, a session scoped managed bean called UserManagedBean
is responsible for tracking the current user, thus it has a property of type User
that is set when someone logs in successfully. A filter is watching the value of this property and redirects to login page if necessary. Of course, both User
and UserManagedBean
are serializable (implementing the interface and not containing anything non-serializable).
My problem is that after the successful login, refreshing the page throws me back to the login page AND my previously set user
property is now null (actually this is why the filter triggers the redirect).
Here is what I tried:
- Separated logic and data: UserManagedBean now only has this one property and a few helper methods, no EJBs, nothing related to Java magic.
- Tried setting the
javax.faces.STATE_SAVING_METHOD
context parameter in the web.xml to both server and client, nothing changed. - Verified that the session scoped managed bean remains the same: only one of it is created, but somehow the value of user is nulled after navigating from the login page.
- According to the NetBeans debugger, the
user
field is not accessed apart from setting it to the logged in user. - Specifying custom serialization methods revealed that UserManagedBean is not serialized or deserialized during the experiment.
- Debugging in Chrome suggests that the session ID is saved in the cookie and is not changed when the value of
user
is lost. - No exceptions are detected.
I must be missing something trivial, any help would be appreciated.
(UPDATE: It was indeed trivial and not related to JSF or managed beans, see my answer below.)
My code is the following:
User
class:
@Entity(name = "USERS")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String username;
private boolean administrator;
private byte[] salt;
private byte[] passwordHash;
public String getUsername() {
return username;
}
public void setUsername(String userName) {
this.username = userName;
}
public boolean isAdministrator() {
return administrator;
}
public void setAdministrator(boolean administrator) {
this.administrator = administrator;
}
public byte[] getSalt() {
return salt;
}
public void setSalt(byte[] salt) {
this.salt = salt;
}
public byte[] getPasswordHash() {
return passwordHash;
}
public void setPasswordHash(byte[] passwordHash) {
this.passwordHash = passwordHash;
}
@Override
public int hashCode() {
int hash = 0;
hash += (username != null ? username.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof User)) {
return false;
}
User other = (User) object;
if ((this.username == null && other.username != null) || (this.username != null && !this.username.equals(other.username))) {
return false;
}
return true;
}
@Override
public String toString() {
return "hu.bme.aut.mv.testbay.ejb.entities.User[ id=" + username + " ]";
}
}
UserManagedBean
class:
@ManagedBean(name = "userManagedBean")
@SessionScoped
public class UserManagedBean implements Serializable {
private static final long serialVersionUID = 1L;
private User currentUser;
public User getCurrentUser() {
return currentUser;
}
public void setCurrentUser(User user) {
this.currentUser = user;
}
public boolean isLoggedIn() {
return currentUser != null;
}
public boolean isAdmin() {
return currentUser != null && currentUser.isAdministrator();
}
public String logout() {
currentUser = null;
return "/faces/index.xhtml";
}
/**
* Creates a new instance of UserManagedBean
*/
public UserManagedBean() {
System.out.println("UserManagedBean constructed!");
}
}
The filter (doBeforeProcessing
):
HttpSession session = ((HttpServletRequest) request).getSession(false);
UserManagedBean userManagedBean = (session != null) ? (UserManagedBean) session.getAttribute("userManagedBean") : null;
if (userManagedBean == null || userManagedBean.getCurrentUser() == null) {
((HttpServletResponse)response).sendRedirect(((HttpServletRequest) request).getContextPath() + "/faces/login.xhtml");
}
UPDATE:
It is important to note that the user is properly set once, and transition to the welcome screen happens as it should. However, the nex request finds the user property empty.
The code triggering the authentication is in the request scoped LoginManagedBean
class:
@ManagedBean
@RequestScoped
public class LoginManagedBean implements Serializable {
@EJB
private AuthenticationSessionBeanLocal authBean;
@ManagedProperty("#{userManagedBean}")
private UserManagedBean userManagedBean;
@PostConstruct
public void Dummy() {
User user = userManagedBean.getCurrentUser();
}
public UserManagedBean getUserManagedBean() {
return userManagedBean;
}
public void setUserManagedBean(UserManagedBean userManagedBean) {
this.userManagedBean = userManagedBean;
}
private String username;
private String password;
//Some getters and setters...
//...
public String login() throws NoSuchAlgorithmException {
if (authenticate(username, password)) {
if (userManagedBean.getCurrentUser().isAdministrator())
return "/faces/admin/welcome.xhtml?faces-redirect=true";
else
return "/faces/testing/welcome.xhtml?faces-redirect=true";
}
return null;
}
private boolean authenticate(String username, String password) throws NoSuchAlgorithmException {
userManagedBean.setCurrentUser(authBean.authenticate(username, password));
if (userManagedBean.getCurrentUser() == null)
return false;
return true;
}
//Constructor and methods...
//...
}
session.getAttribute("userManagedBean")
. Where did you set the attribute before getting it out? – Mr.J4mes