Java 绑定用户实体和GlassFish主体

Java 绑定用户实体和GlassFish主体,java,jsf,jakarta-ee,glassfish,Java,Jsf,Jakarta Ee,Glassfish,我有一个实体类User,其中包含用户名、名字、姓氏和密码等信息,我有GlassFish 3.1服务器设置来执行身份验证。到目前为止,一切顺利。容器对用户进行身份验证后,我需要某种方法将主体绑定到实际的用户实体。毕竟,GlassFish告诉我的是,用户“laurens”已经过身份验证,它没有给我相应的用户实体 为此,我编写了一个JSF管理的beanUserController。我想知道的是,这是否是查找实际实体的正确方法,以及是否存在我没有看到的任何明显缺陷 UserController具有以下字

我有一个实体类
User
,其中包含用户名、名字、姓氏和密码等信息,我有GlassFish 3.1服务器设置来执行身份验证。到目前为止,一切顺利。容器对用户进行身份验证后,我需要某种方法将主体绑定到实际的用户实体。毕竟,GlassFish告诉我的是,用户“laurens”已经过身份验证,它没有给我相应的
用户
实体

为此,我编写了一个JSF管理的bean
UserController
。我想知道的是,这是否是查找实际实体的正确方法,以及是否存在我没有看到的任何明显缺陷

UserController
具有以下字段:

@EJB
private UserFacade userFacade;

private User user;
userFacade
是一个无状态会话bean,用于持久化和查找
User
实例。JSF页面使用
user
字段来获取和设置用户的属性

我使用以下方法执行绑定,并附带两个助手方法:

@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);
}
这是正确的方法吗

谢谢

编辑:

谢谢你的投入!我考虑了一下,虽然我认为现在把密码移到另一个表上对我来说有点太难处理了,我确实认为我可以通过在
@RequestScoped
和精简的
@SessionScoped
中分离
用户控制器来解决一些问题


AuthenticationController
将具有
emailAddress
password
字段,由网页的emailAddress和password字段绑定。它还将包含
public void authenticate()
,用于对用户进行身份验证并在之后丢弃凭据。然后,
@SessionScoped
UserController
可以绑定到相应的
User
实体,而无需知道密码。事实上,我相信我将能够从
用户
中删除密码字段。

您提出的方法有一些粗糙之处,但在大多数情况下,它是相当好的

如果您打算存储对
用户
实体的引用,那么最好在
会话范围内的
托管bean中这样做。这有它的优点和缺点。明显的优势在于

  • 用户
    实体在整个应用程序流程中都可以在所有页面中使用。这意味着您只需要为会话将
    主体
    绑定到
    用户
    实体一次。如果需要,您可以在所有页面中重复使用绑定值
不太明显的缺点是

  • password
    字段将在内存中存储相当长的时间。充其量,您应该在身份验证尝试后尝试使实体的密码字段为空(无论是否失败,无论该字段是否包含明文或哈希形式的密码)。另外,将
    password
    字段定义为LAZY-fetched(
    FetchType
    LAZY
    )与默认的eager-fetch(
    eager
    FetchType
    )相反,这将非常有意义。如果您实现了这一点(特别是密码字段的无效),则需要注意涉及在
    用户
    实体上执行的合并操作的问题;在这种情况下,最好有一个单独的实体来为用户存储密码(很不幸,但在某些应用程序中,您必须在一定程度上保护密码及其哈希)
尽管如此,还必须确保:

  • 应该小心处理匿名用户主体。如果您没有编写一个过滤器来强制执行访问控制机制以保护应用程序的“私有”页面,那么您应该更加担心页面中授权逻辑的构造方式,而不是在使用过滤器时根本不必担心。匿名主体与任何其他主体一样,只是它不受域中标识的支持。如果主体到用户实体绑定方案因某种原因失败,则必须使会话无效,并将用户重新重定向到登录页面,尤其是当您的页面依赖于
    用户
    实体而不是
    主体
    对象来强制执行访问控制检查时
  • 确保应用程序有单独的登录页面。在大多数应用程序中,这是最好的,这些应用程序以登录页面中显示的形式接受用户凭据;如果表单位于对话框或其他装置中,通常不需要单独的登录页面。这是可取的,原因很简单,您希望您的登录过程实现POST-REDIRECT-GET模式-成功登录到应用程序的用户必须重定向到应用程序的主页面。否则,将导致浏览器刷新(由有权访问终端的任何人执行)将重新提交凭据的情况;很明显,使用模式对话框或类似对话框的应用程序不太容易出现此问题
更新

这是基于编辑的问题。如果按照建议实施
authenticate
方法,则只有在认证成功后,才能实施将
用户
实体绑定到
主体
的方案

Th
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>