JSF: атрибут управляемого bean-компонента с областью сеанса становится нулевым

Я играю с JavaEE 7 и пытался написать веб-приложение с простым механизмом входа в систему.

Существует класс сущности EJB с JPA, который называется User, содержащий данные о пользователях. В WAR управляемый bean-компонент с областью сеанса с именем UserManagedBean отвечает за отслеживание текущего пользователя, поэтому он имеет свойство типа User, которое устанавливается, когда кто-то успешно входит в систему. Фильтр отслеживает значение этого свойства и при необходимости перенаправляет на страницу входа. Конечно, и User, и UserManagedBean сериализуемы (реализуют интерфейс и не содержат ничего несериализуемого).

Моя проблема в том, что после успешного входа в систему обновление страницы возвращает меня на страницу входа, И мое ранее установленное свойство user теперь имеет значение NULL (на самом деле, именно поэтому фильтр запускает перенаправление).

Вот что я пробовал:

  • Разделение логики и данных: UserManagedBean теперь имеет только одно свойство и несколько вспомогательных методов, никаких EJB-компонентов и ничего, связанного с магией Java.
  • Пытался установить параметр контекста javax.faces.STATE_SAVING_METHOD в web.xml как для сервера, так и для клиента, ничего не изменилось.
  • Проверено, что управляемый bean-компонент с привязкой к сеансу остается прежним: создается только один из них, но каким-то образом значение 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");
}

ОБНОВЛЕНИЕ:

Важно отметить, что пользователь правильно настроен один раз, и переход к экрану приветствия происходит должным образом. Однако запрос nex обнаруживает, что свойство пользователя пусто.

Код, запускающий аутентификацию, находится в классе 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...
    //...
}

person Vincent    schedule 12.05.2014    source источник
comment
мне непонятно, как вы используете фильтр. Обычно страница входа в систему представляет собой страницу без пользователя с ограниченным объемом запроса, а действие входа в систему вызывает код фильтра для создания / извлечения объекта User и назначения его UserManagerBean в области сеанса.   -  person Leo    schedule 13.05.2014
comment
Другой управляемый компонент с ограниченным объемом запроса выполняет аутентификацию и извлекает объект User с помощью EJB, устанавливая его в UserManagedBean, который присутствует все время. Фильтр активируется, когда запрашивается страница, отличная от логина или формы регистрации, и проверяет, содержит ли экземпляр UserManagedBean объект User или нет. Выход из системы устанавливает для этого свойства значение null (и, по-видимому, что-то еще делает то же самое непреднамеренно).   -  person Vincent    schedule 13.05.2014
comment
Как вы переходите с экрана входа на главную страницу с аутентификацией?   -  person Leo    schedule 13.05.2014
comment
@Leo, пожалуйста, посмотрите мое обновление.   -  person Vincent    schedule 13.05.2014
comment
session.getAttribute("userManagedBean"). Где вы установили атрибут перед тем, как получить его?   -  person Mr.J4mes    schedule 13.05.2014
comment
Поскольку это управляемый компонент с ограниченным сеансом, он создается при первой ссылке, и каждая страница ссылается на него. Между прочим, это не null, а только одно из его свойств.   -  person Vincent    schedule 13.05.2014


Ответы (2)


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

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

Единственное, что меня интересует, это то, как NetBeans не смог нарушить как метод set, так и точку останова поля, которая должна была сработать при доступе к полю.

person Vincent    schedule 13.05.2014

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

person Laura Liparulo    schedule 04.10.2017