JSF: атрибут управляемого Бина с областью действия сеанса становится нулевым
Я играю с JavaEE 7 и попытался написать веб-приложение с простым механизмом входа в систему.
Существует класс сущностей EJB, сохраняемый с JPA под названием User
, содержащий данные о пользователях. В войне управляемый боб с областью действия сеанса UserManagedBean
отвечает за отслеживание текущего пользователя, поэтому он имеет свойство типа User
, которое устанавливается, когда кто-то успешно входит в систему. Фильтр следит за значением этого свойства и при необходимости перенаправляет на страницу входа. Конечно, и User
, и UserManagedBean
являются сериализуемыми (реализующими интерфейс и не содержащими ничего несериализуемого).
Моя проблема заключается в том, что после успешного входа в систему обновление страницы возвращает меня на страницу входа, и мое ранее установленное СВОЙСТВО user
Теперь равно null (на самом деле именно поэтому фильтр запускает перенаправление).
Вот что я попробовал:
- разделенная логика и данные: UserManagedBean теперь имеет только это свойство и несколько вспомогательных методов, без EJBs, ничего общего с яванской магией.
- попытался установить параметр контекста
javax.faces.STATE_SAVING_METHOD
в интернете.xml для сервера и клиента, ничего не изменилось. - проверено, что управляемый компонент с областью действия сеанса остается неизменным: создается только один из них, но каким-то образом значение user обнуляется после перехода со страницы входа.
- Согласно отладчику NetBeans, к полю
user
нельзя получить доступ, кроме как установив его для вошедшего пользователя. - указание пользовательской сериализации методы показали, что UserManagedBean не сериализуется или десериализуется во время эксперимента. Отладка в Chrome предполагает, что идентификатор сеанса сохраняется в файле cookie и не изменяется при потере значения
- никаких исключений не обнаружено.
user
.
(обновление: это было действительно тривиально и не связано с JSF или управляемыми бобами , см. Мой ответ под.)
Мой код следующий:
User
класс:
@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
класс:
@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!");
}
}
Фильтр (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");
}
Обновление:
Важно отметить, что пользователь правильно настроен один раз, и переход к экрану приветствия происходит так, как и должно. Однако, запрос Некс находит собственностью пользователей пуст.Код, запускающий аутентификацию, находится в классе LoginManagedBean
области запроса:
@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...
//...
}
2 ответа:
Это действительно была глупая ошибка.
Точки останова NetBeans не сигнализировали об этом, но к полю действительно обращалась функция выхода из системы, которая вызывалась после каждой навигации. Оказалось, что я неправильно использовал компонент PrimeFaces. Я хотел установить атрибутoutcome
метода logoutcommandButton
для метода logout наUserManagedBean
, но NetBeans autocomplete поставил пару скобок после имени метода, который я не заметил. Поэтому вместо того, чтобы получить ошибку, что атрибутoutcome
не работает подобноaction
и должен получить путь в виде строки, EL оценил методlogout()
, чтобы установить результат на возвращенный url страницы входа, но метод также вышел из пользователя молча. Единственное, что меня удивляет, это то, как NetBeans не удалось сломать как метод set, так и точку останова поля, которая должна была сработать при обращении к полю.