Лучший способ войти, выйти и сохранить атрибуты сеанса в JSF - PullRequest
2 голосов
/ 22 января 2012

Я очень новичок в JSF, и меня довольно смущают лучшие практики управления пользовательскими логинами.Есть много ответов, но они требуют знаний JSF и Java EE, которых у меня, похоже, нет.

При работе с классическим JSP-проектом я очень легко обрабатываю логины.Когда пользователь успешно входит в систему, создается атрибут сеанса isLoggedIn со значением true.Когда пользователь выходит из системы, сеанс становится недействительным.Если атрибут «isLoggedIn» имеет значение false или отсутствует, страницы, требующие входа в систему, перенаправляют на экран входа в систему.

Как это можно было бы сделать в JSF?Должен ли я хранить такие вещи, как статус входа, имя пользователя и роль пользователя, в HttpSession или в управляемом компоненте сеанса?

Множество ответов, которые я нашел, говорят, что вы должны использовать request.getRemoteUser () для управления входами в систему., но не давайте никаких дальнейших объяснений.Из того, что я смог собрать, этот метод полезен, если вы используете учетные данные домена для входа в приложение.Так полезно ли, если я храню учетные данные пользователя (имя пользователя + соленый и хешированный пароль) в базе данных?

Было бы очень полезно, если бы кто-нибудь показал мне, как веб-сайт со следующими двумя страницами будетработа:

  1. Страница входа, на которой можно ввести имя пользователя и пароль и нажать кнопку «Войти».
  2. Страница с сообщением «Войти успешно» и кнопкой выхода из системы.,Когда кнопка выхода из системы нажата, пользователь выходит из системы и переносится на страницу входа.

Если пользователь пытается перейти на вторую страницу, пока он не вошел в систему, он будет перенаправленна первую страницу.

Как я уже говорил, я новичок в JSF и просто не могу найти хороший учебник о том, как справляться с подобными вещами.Поэтому любые ссылки, которые вы считаете полезными, будут оценены.

Если это имеет значение, я использую реализацию MyFaces и библиотеку компонентов PrimeFaces.

1 Ответ

6 голосов
/ 22 января 2012

У вас есть два основных варианта:

  1. Использовать аутентификацию на основе контейнеров.
  2. Свернуть свой.

Первый вариант официально рекомендованподход, и точные детали будут зависеть от того, какой контейнер сервлета и / или веб-фреймворк вы используете.

Честно говоря, однако, я часто нахожу, что настройка аутентификации на основе контейнеров доставляет больше хлопот, чем стоит (и больше хлопот, чем простое создание пользовательского слоя аутентификации, который делает то, что я хочу).Поэтому, если вы заинтересованы в развертывании своего собственного подхода, я обычно использую подход (использующий Spring и Hibernate), чтобы иметь класс User, например:

@Entity
@Table(name="users")
@NamedQueries({
    @NamedQuery(name="User.findAll", query="SELECT u FROM User u"),
    @NamedQuery(name="User.findByPrimaryEmail", query="SELECT u FROM User u WHERE u.primaryEmail = :email")
})
public class User {
    //fields
    private long id;
    private String primaryEmail;
    private String firstName;
    private String lastName;
    private String hashedPassword;
    private String salt;
    //...

    //relationships
    //...

    public User() {
        primaryEmail = null;
        firstName = null;
        lastName = null;
        salt = null;
        hashedPassword = null;
        //...
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }

    @Column(unique=true)
    public String getPrimaryEmail() {
        return primaryEmail;
    }
    public void setPrimaryEmail(String email) {
        this.primaryEmail = email;
        if (this.primaryEmail != null) {
            this.primaryEmail = email.toLowerCase();
        }
    }

    @Column
    public String getHashedPassword() {
        return hashedPassword;
    }
    public void setHashedPassword(String hashedPassword) {
        this.hashedPassword = hashedPassword;
    }

    @Column
    public String getSalt() {
        return salt;
    }
    public void setSalt(String salt) {
        this.salt = salt;
    }

    //(getters and setters for any other columns and relationships)

    @Transient
    public void setPassword(String passwordPlaintext) throws NoSuchAlgorithmException, InvalidKeySpecException {
        if (this.getSalt() == null) {
            this.setSalt(StringUtilities.randomStringOfLength(16));
        }

        this.setHashedPassword(this.computeHash(passwordPlaintext, this.getSalt()));
    }

    @Transient
    public boolean checkPasswordForLogin(String passwordPlaintext) throws NoSuchAlgorithmException, InvalidKeySpecException {
        if (StringUtilities.isEmpty(passwordPlaintext)) {
            return false;
        }
        return this.getHashedPassword().equals(this.computeHash(passwordPlaintext, this.getSalt()));
    }

    @Transient
    private String computeHash(String password, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 2048, 160);
        SecretKeyFactory fact = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

        //I encode to base64 so that I can treat the hash as text in computations and when storing it in the DB
        return Base64.encodeBytes(fact.generateSecret(spec).getEncoded());
    }
}

, а затем простую форму входа, например:

<form id="loginForm" method="POST" action="/r/submitLogin">
    <div class="formRow">
        <span class="formLabel">Email</span>  <input type="text" class="textInput" name="email" />
    </div>
    <div class="formRow">
        <span class="formLabel">Password</span>  <input type="password" class="textInput" name="pass" />
    </div>
    <div class="formRow">
        <input type="submit" class="submitButton" value="Log In" />
    </div>
</form>

И реализация submitLogin, которая выглядит следующим образом:

public ModelAndView submitLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String email = request.getParameter("email");
    String pass = request.getParameter("pass");

    EntityManager em = DatabaseUtil.getEntityManager(request);  //get an EntityManager, you can also use dependency-injection to do this if you prefer
    User user = getUserByPrimaryEmail(email, em);  //lookup the user by email address
    if (user == null) {
        //invalid username
        request.setAttribute("error", "User not found");
        return login(request, response);
    }

    try {
        if (user.checkPasswordForLogin(pass)) {
            //valid login, remember the user in the session
            request.getSession().setAttribute(Constants.SESSION_USER_KEY, user);

            //send the user to the default page
            response.sendRedirect("/r/indexPage");
            return null;
        }
        else {
            //invalid password
            request.setAttribute("error", "Incorrect password");
            return login(request, response);
        }
    }
    catch (Exception e) {
        //should only happen if checkPasswordForLogin() throws NoSuchAlgorithmException/InvalidKeySpecException
        LOG.error("Login processing failed!", e);
        request.setAttribute("error", "Cannot generate password hash?!?!?");
        return login(request, response);
    }
}

Это все, что нужно, для базовой реализации в любом случае.Вы можете, конечно, создать такие функции, как куки-файлы "Запомнить меня" / постоянный вход в систему, роли пользователей, статусы, уровни доступа и т. Д.Но для базового входа в систему / выхода из системы это все, что вам действительно нужно.

При таком подходе вошедший в систему пользователь будет доступен с помощью команды request.getSession().getAttribute(Constants.USER_KEY), где Constants.USER_KEY - это просто некоторая произвольная строка, которую вы определяете.Я обычно использую что-то вроде " .user".

Если используется аутентификация на основе контейнеров, пользователь, как правило, будет отображаться путем вызова request.getUserPrincipal().

...