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 3

2 ответа:

Это действительно была глупая ошибка.

Точки останова NetBeans не сигнализировали об этом, но к полю действительно обращалась функция выхода из системы, которая вызывалась после каждой навигации. Оказалось, что я неправильно использовал компонент PrimeFaces. Я хотел установить атрибут outcome метода logout commandButton для метода logout на UserManagedBean, но NetBeans autocomplete поставил пару скобок после имени метода, который я не заметил. Поэтому вместо того, чтобы получить ошибку, что атрибут outcome не работает подобно action и должен получить путь в виде строки, EL оценил метод logout(), чтобы установить результат на возвращенный url страницы входа, но метод также вышел из пользователя молча. Единственное, что меня удивляет, это то, как NetBeans не удалось сломать как метод set, так и точку останова поля, которая должна была сработать при обращении к полю.

Вы можете поместить переменную в представление (то есть область представления) и извлечь ее / изменить с помощью геттера и сеттера. Это также позволит вам лучше протестировать контроллер, высмеивая реакцию представления.