Связывание сущности пользователя и принципала GlassFish - PullRequest
7 голосов
/ 25 июля 2011

У меня есть класс сущностей User, который содержит такую ​​информацию, как имя пользователя, имя, фамилия и пароль, и у меня есть настройки сервера GlassFish 3.1 для выполнения аутентификации. Все идет нормально. После того, как контейнер аутентифицировал пользователя, мне нужно каким-то образом связать принципал с действительным объектом User. В конце концов, GlassFish говорит мне, что пользователь "laurens" прошел аутентификацию, он не дает мне соответствующую User сущность.

Для этого я написал управляемый компонент JSF UserController. Я хотел бы знать, является ли это правильным способом поиска реальной сущности, и есть ли какие-либо очевидные ловушки, которых я не вижу.

UserController имеет следующие поля:

@EJB
private UserFacade userFacade;

private User user;

userFacade - это сеансный компонент без сохранения состояния, который сохраняется и находит User экземпляры. Поле user используется страницей JSF для получения и установки свойств пользователя.

Я использую следующий метод для выполнения привязки, сопровождаемый двумя вспомогательными методами:

@PostConstruct
private void init() {
    try {
        user = userFacade.find(getUserPrincipal().getName());
    } catch (NullPointerException ex) {
        // Intentionally left empty -- User is not logged in.
    }
}

private HttpServletRequest getHttpServletRequest() {
    return (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
}

private Principal getUserPrincipal() {
    return getHttpServletRequest().getUserPrincipal();
}

Следующие методы используются страницей JSF для определения того, какие компоненты отображать (если пользователь уже аутентифицирован, тогда нет необходимости показывать форму входа), аутентифицировать пользователя, если нажата кнопка «Войти», или зарегистрироваться как новый пользователь при нажатии кнопки «Зарегистрироваться».

public boolean isAuthenticated() {
    return getUserPrincipal() != null;
}

public void authenticate() {
    try {
        getHttpServletRequest().login(user.getEmailAddress(), user.getPassword());
    } catch (Exception ex) {
        // TODO: Handle failed login attempt
    }
}

public void register() {
    userFacade.create(user);
}

Правильный ли это путь?

Спасибо!

Edit:

Спасибо за вклад обоих! Я немного подумал об этом, и хотя я думаю, что перенести пароли в другую таблицу для меня пока слишком сложно, я думаю, что могу решить некоторые проблемы, выделив UserController в @RequestScoped AuthenticationController и урезанный @SessionScoped UserController.

AuthenticationController будет иметь поля emailAddress и password, связанные полями emailAddress и пароля веб-страницы. Кроме того, он будет содержать public void authenticate() для аутентификации пользователя и последующего удаления учетных данных. Затем @SessionScoped UserController может связываться с соответствующим объектом User без необходимости знать пароль. На самом деле, я считаю, что смогу удалить поле пароля из User в целом.

1 Ответ

3 голосов
/ 25 июля 2011

У вашего предложенного подхода есть несколько неровных краев, но по большей части он довольно хорош.

Если вы намереваетесь сохранить ссылку на сущность User, то предпочтительно сделать это вSessionScoped управляемый боб.В этом есть свои плюсы и минусы.Очевидным преимуществом является то, что

  • сущность User доступна через поток приложений на всех страницах.Это будет означать, что вам нужно связать Principal с User сущностью только один раз для сеанса.И вы можете повторно использовать связанное значение на всех страницах, если возникнет такая необходимость.

Неочевидный недостаток состоит в том, что

  • поле passwordбудет храниться в памяти довольно долго.В лучшем случае вы должны попытаться аннулировать поле пароля объекта после попытки аутентификации (независимо от того, неудачно оно или нет, независимо от того, содержит ли поле пароль в открытом виде или хэш).Кроме того, было бы разумно определить поле password как лениво извлеченное (FetchType из LAZY), в отличие от значения по умолчанию для активного извлечения (FetchType из EAGER).Если вы реализуете это (в частности, обнуляете поле пароля), вам необходимо следить за проблемами, связанными с операциями слияния, выполняемыми с объектом User;в таком случае, возможно, было бы лучше иметь отдельную сущность для хранения паролей для пользователей (весьма прискорбно, но это та степень, в которой вы должны отгибаться назад, чтобы защитить пароль и их хэши в определенных приложениях).

Сказав это, необходимо также обеспечить следующее:

  • С анонимным пользователем следует обращаться осторожно.Если вы не пишете фильтр, который обеспечивает механизм контроля доступа для защиты ваших «личных» страниц приложения, вам следует гораздо больше беспокоиться о том, как создается логика авторизации на ваших страницах, а не о том, что вам не нужно беспокоиться овсе, когда вы используете фильтр.Принципал Anonymous похож на любой другой принципал, за исключением того, что он не поддерживается идентичностью в области.Если по какой-либо причине схема привязки сущности Принципал-Пользователь не срабатывает, необходимо аннулировать сеанс и снова перенаправить пользователя на страницу входа, особенно если для обеспечения контроля доступа ваши страницы используют сущность User вместо объекта Principal.проверяет.
  • Убедитесь, что у вас есть отдельная страница входа для приложения.Это предпочтительно в большинстве приложений, которые принимают учетные данные пользователя в форме, представленной на странице входа в систему;если форма находится в диалоговом окне или в каком-то другом устройстве, отдельная страница входа обычно не требуется.Это желательно по той простой причине, что вы хотите, чтобы ваш процесс входа в систему реализовал шаблон POST-REDIRECT-GET - пользователь при успешном входе в ваше приложение должен быть перенаправлен на главную страницу приложения.Невыполнение этого условия приведет к сценарию, когда обновление браузера (выполняемое любым лицом, имеющим доступ к терминалу) повторно отправит учетные данные;совершенно очевидно, что приложения, использующие модальное диалоговое окно или что-то подобное, менее подвержены этой проблеме.

Обновление

Это основано на отредактированном вопросе.Если вы реализуете метод authenticate в соответствии с предложением, вы можете реализовать свою схему привязки сущности User к Principal только после успешной аутентификации.

Ниже приведено воспроизведение аналогичной реализации из моегоapplication:

public String authenticate()
{
    String result = null;
    ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
    HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
    try
    {
        request.login(userId, password);
        result = "/private/MainPage.xhtml?faces-redirect=true";
    }
    catch (ServletException ex)
    {
        logger.error("Failed to authenticate user.", ex);
        FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, Messages.getString("Login.InvalidIdOrPasswordMessage"), null);
        FacesContext.getCurrentInstance().addMessage(null, facesMessage);
    }
    return result;
}

, которое вызывается из лицевой стороны:

<h:form id="LoginForm" acceptcharset="UTF-8">
    <p>
        <h:outputLabel for="userid" value="#{msg['Login.userid.label']}" />
        <h:inputText id="userid" value="#{loginBean.userId}" />
    </p>
    <p>
        <h:outputLabel for="password" value="#{msg['Login.password.label']}" />
        <h:inputSecret id="password" value="#{loginBean.password}" />
    </p>
        <h:commandButton id="submit" value="#{msg['Login.submit.label']}"
            action="#{loginBean.authenticate}" />
</h:form>

Обратите внимание на использование шаблона POST-REDIRECT-GET для случая, когда аутентификация прошла успешно.Я оставил немного кода, который относится к аннулированию текущего сеанса перед перенаправлением, чтобы предотвратить атаки фиксации сеанса.Привязка сущности User к Principal будет выполняться в новом сеансе при условии, что это делается в bean-объекте, определяемом сессией.

...