3

我正在使用 JavaEE 7 并尝试使用简单的登录机制编写一个 webapp。

有一个使用 JPA 持久化的 EJB 实体类,称为User保存有关用户的数据。在 WAR 中,一个名为 session 范围的托管 beanUserManagedBean负责跟踪当前用户,因此它有一个 type 属性,User当有人成功登录时设置。过滤器正在监视此属性的值,并在必要时重定向到登录页面。当然,两者User都是UserManagedBean可序列化的(实现接口并且不包含任何不可序列化的内容)。

我的问题是,在成功登录后,刷新页面会让我回到登录页面,而我之前设置user的属性现在为空(实际上这就是过滤器触发重定向的原因)。

这是我尝试过的:

  • 分离的逻辑和数据:UserManagedBean 现在只有这个属性和一些辅助方法,没有 EJB,没有任何与 Java 魔法相关的东西。
  • 尝试将javax.faces.STATE_SAVING_METHODweb.xml 中的上下文参数设置为服务器和客户端,没有任何改变。
  • 验证了会话范围的托管 bean 保持不变:只创建了一个,但不知何故,从登录页面导航后,用户的值为空。
  • 根据 NetBeans 调试器,user除了将其设置为登录用户外,不会访问该字段。
  • 指定自定义序列化方法表明 UserManagedBean 在实验期间未序列化或反序列化。
  • 在 Chrome 中调试建议会话 ID 保存在 cookie 中,并且在 的值user丢失时不会更改。
  • 未检测到异常。

我一定错过了一些琐碎的事情,任何帮助将不胜感激。

更新:这确实是微不足道的,与 JSF 或托管 bean 无关,请参阅下面的答案。)

我的代码如下:

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");
}

更新:

重要的是要注意,用户已正确设置一次,并且会正常转换到欢迎屏幕。但是,下一个请求发现 user 属性为空。

触发身份验证的代码在请求范围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...
    //...
}
4

2 回答 2

1

这确实是一个愚蠢的错误。

NetBeans 断点并没有发出信号,但该字段确实由注销函数访问,该函数在每次导航后调用。原来我误用了 PrimeFaces 组件。我想将 logout 的outcome属性设置为 logoutcommandButton上的方法UserManagedBean,但是 NetBeans 自动完成在我没有注意到的方法名称后面放了一对括号。outcome因此,EL 不会收到属性无法正常工作的错误,而是action应该将路径作为字符串接收,而是评估了logout()将结果设置为登录页面返回 url 的方法,但该方法还默默地注销了用户。

我唯一想知道的是,NetBeans 如何未能在 set 方法和访问字段时应该触发的字段断点中中断。

于 2014-05-13T12:26:43.813 回答
0

您可以将变量放在视图中(即视图范围)并使用 getter 和 setter 检索/修改它。这也将允许您通过模拟视图响应以更好的方式测试控制器。

于 2017-10-04T09:09:18.377 回答