0

我正在使用 JSF 创建一个 Timesheet 应用程序,目前正在处理登录部分。这是一个直接的登录,它需要一个登录 ID 和一个密码,并检查它是否是一个有效的组合(尽管此时我只是在进行管理员登录)。

我的问题似乎是 JSF 在调用验证操作方法之前没有更新我的 loginID 和密码字段,因为当我单击登录时,它会引发 NullPointerException。这是我的验证方法。

/**
 * Verifies that the loginID and password is the super user combination.
 * @return true if it is, false if it is not.
 */
public String verifyUser() {
    ResourceBundle login = getSuperUserLogin();
    String user = getLoginID();
    String checkPassword = getPassword();
    if(!login.containsKey(user) || !checkPassword.equals(login.getString(user))) {
        return "regular";
    }
    return "super";
}

和我的登录页面

<body>
    <div class = "content" align = "center">
        <h:form>
            <h3>#{msgs.loginTitle}</h3>
            <h:panelGrid columns="2" cellpadding = "4">
                    <h:outputText value = "#{msgs.userID}" styleClass="title" />
                    <h:inputText value="#{user.loginID}" styleClass="rounded"  />
                    <h:outputText value = "#{msgs.password}" styleClass="title" />
                    <h:inputSecret value="#{user.password}" styleClass="rounded" />
             </h:panelGrid>
             <p><h:commandButton value="#{msgs.login}" styleClass="button" action="#{superUser.verifyUser}"/></p>
          </h:form>
      </div>

现在,如果我将登录 ID 和密码硬编码到用户和 checkPassword 字段中,那么它就没有问题了。所以我认为问题在于 loginID 和 password 字段为空(因为 getLoginID() 和 getPassword() 只是返回这些字段的常规旧 getter)。我知道答案会很愚蠢,我会因为错过它而感到尴尬,但此时我的头撞到了墙上。

只要信息完整,loginID 和密码字段位于 User 类中(提供了适当的设置器),而 verifyUser 方法位于子类 SuperUser 中。我不知道为什么这会很重要,但这将是我第一个使用超类和子类的 JSF 应用程序,所以你永远不会知道。

编辑: 根据要求,这是我的用户类代码

package ca.bcit.infosys.timesheet.model;
import java.util.ResourceBundle;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

@Named
@ApplicationScoped
public class User implements java.io.Serializable {
/** The user's name */
private String name;
/** The user's employee number */
private int empNumber;
/** The user's login ID */
private String loginID;
/** The user's password */
private String password;
/** Is the user currently in an editable state (i.e. the user's information can be edited on the webpage) */
private boolean editable;
private ResourceBundle superUserLogin = ResourceBundle.getBundle("ca.bcit.infosys.timesheet.model.messages");

/**
 * @return the name
 */
public String getName() {
    return name;
}

/**
 * @param name the name to set
 */
public void setName(String name) {
    this.name = name;
}

/**
 * @return the empNumber
 */
public int getEmpNumber() {
    return empNumber;
}

/**
 * @param empNumber the empNumber to set
 */
public void setEmpNumber(int empNumber) {
    this.empNumber = empNumber;
}

/**
 * @return the loginID
 */
public String getLoginID() {
    return loginID;
}

/**
 * @param loginID the loginID to set
 */
public void setLoginID(String loginID) {
    this.loginID = loginID;
}

/**
 * @return the password
 */
public String getPassword() {
    return password;
}

/**
 * @param password the password to set
 */
public void setPassword(String password) {
    this.password = password;
}

/**
 * @return the isEditable
 */
public boolean isEditable() {
    return editable;
}

/**
 * @param isEditable the isEditable to set
 */
public void setEditable(boolean editable) {
    this.editable = editable;
}

/**
 * @return the Property containing the super user login information.
 */
public ResourceBundle getSuperUserLogin() {
    return superUserLogin;
}

}

4

1 回答 1

3

您当前的方法存在很多误解。我建议首先学习并掌握 JSF 的基本概念来完成此任务。

为了完成这项工作,您需要一个 bean,它只需要对服务器的每个请求都处于活动状态。该 bean 将负责从 JSF 表单中获取数据并执行验证操作。然后,您只需要将字段和登录方法绑定到您的 JSF 表单。

Java 代码

//Looks like you use CDI (or probably you just think this is what you need)
@Named
//The bean just needs to be alive on each request
//Since you're using CDI, make sure this annotation comes from
//javax.enterprise.context package
@RequestScoped
//Usually, the managed beans used in JSF are named with "Bean" suffix
//Since it is for learning purposes, I'll name this bean UserBean
public class UserBean {
    //fields to hold username and password
    //they both are Strings
    private String username;
    private String password;
    //getters and setters methods
    //I won't add them since it is boilerplate code for this sample
    //...

    //Method to handle your login action
    //If successful login, then returns the name of the next view
    //If unsuccessful, returns null to stay in the current view
    public String login() {
        //Sample implementation, remember to change it for A REAL ONE
        if ("admin".equals(username) &&
            "adminPass".equals(password)) {
            //Successful login
            //Make sure to have a file index.xhtml in the same folder
            //where the login JSF page resides (for sample purposes)
            return "index";
        }
        //Unsuccessful login
        //Also, it is a good idea to show a message for end user
        FacesMessage message = new FacesMessage("Invalid login.");
        FacesContext.getCurrentInstance().addMessage(null, message);
        return null;
    }
}

JSF 代码

<h:form>
    <h3>Login Title</h3>
    <h:panelGrid columns="2">
        <h:outputText value="User Id" />
        <h:inputText value="#{userBean.username}" />
        <h:outputText value="Password" />
        <h:inputSecret value="#{userBean.password}" />
    </h:panelGrid>
    <h:commandButton value="Login" action="#{userBean.login}" />
    <br />
    <h:messages />
</h:form>

在继续之前您需要了解的一些事项:

  • JSF beanCDI bean 不同。此处对此进行了更好的解释:Backing beans (@ManagedBean) or CDI Beans (@Named)? . 我建议阅读 Bozho 和 BalusC 的答案。
  • 定义托管 bean 的范围。JSF 初学者面临的主要问题之一是为 bean 选择正确的范围。例如,您开始将 bean 标记为,这意味着整个应用程序@ApplicationScoped只有一个 User bean ,这意味着两个不同的最终用户将拥有相同的用户对象(现在您开始担心了)。您可以在此处了解更多信息:如何选择正确的 bean 范围?
  • 正如您从上面的链接中看到的那样,有一个视图范围。如果您使用的是 Java EE 7,那么您会发现此范围也可用于您的 CDI bean。如果您使用的是 Java EE 6,那么您会发现没有注释。但别担心,OmniFaces从 1.6 版开始就解决了这个问题,并带有@ViewScoped来自 package.json 的自定义注释org.omnifaces.cdi
  • 由于您是从 JSF/CDI 开始的,因此我建议每个视图使用一个 bean。当您有更多经验时,您可能会在同一视图中使用两个或更多 bean。这是一个很好的例子:我可以在同一个 xhtml 页面中使用多个托管 bean 吗?
于 2013-10-27T08:25:41.590 回答