绑定用户实体和GlassFish Principal
我有一个实体类User
,它包含用户名,名字,姓氏和密码等信息,并且我的GlassFish 3.1服务器设置可以执行身份验证。 到现在为止还挺好。 容器验证用户后,我需要一些方法将主体绑定到实际的用户实体。 毕竟,GlassFish告诉我,用户“劳伦斯”已经过身份验证,并不是给我相应的User
实体。
为此,我编写了一个JSF托管bean UserController
。 我想知道的是,如果这是查看实际实体的正确方法,以及是否有任何明显的陷阱我没有看到。
UserController
具有以下字段:
@EJB
private UserFacade userFacade;
private User user;
userFacade
是一个无状态会话bean,用于保存并查找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);
}
这是否是正确的方式?
谢谢!
编辑:
感谢您的输入! 我想了一下,虽然我认为将密码移动到不同的表格对于我来说目前处理得有点多,但我认为我可以通过在@RequestScoped
分离UserController
来解决一些问题AuthenticationController
和一个精简的@SessionScoped
UserController
。
AuthenticationController
将具有emailAddress
和password
字段,由网页的emailAddress和密码字段绑定。 它还会包含public void authenticate()
来验证用户身份,然后丢弃凭证。 @SessionScoped
UserController
然后可以绑定到适当的User
实体,而不需要知道密码。 事实上,我相信我将能够从User
一起删除密码字段。
你提出的方法有一些粗糙的边缘,但大多数情况下它很好。
如果您打算存储对User
实体的引用,那么最好在SessionScoped
托管bean中这样做。 这有利有弊。 显而易见的优点是
User
实体通过应用程序流遍历所有页面。 这意味着您只需将会话的Principal
绑定到User
实体一次。 如果需要,您可以通过所有页面重新使用绑定值。 不那么明显的缺点是
password
字段将存储在内存中相当长的时间。 充其量,在尝试进行身份验证(无论是否成功,无论该字段是否包含明文或散列密码)时,您都应尝试取消实体的密码字段。 另外,将password
字段定义为FetchType
fetched( FetchType
of LAZY
),而不是默认的FetchType
( EAGER
FetchType
)。 如果您执行此操作(特别是取消密码字段),则需要注意涉及在User
实体上执行的合并操作的问题; 在这种情况下,让一个单独的实体为用户存储密码可能会更好(非常不幸,但这是在某些应用程序中必须弯腰以保护密码和哈希的程度)。 话虽如此,还有必要确保以下几点:
User
实体而不是Principal
对象来强制进行访问控制检查。 更新
这是基于编辑的问题。 如果按照建议实施authenticate
方法,则只有在成功验证后,才能实现将User
实体绑定到Principal
方案。
以下是我的应用程序中的类似实现的复制:
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;
}
这是从facelet调用的:
<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模式。 在重定向之前,我留下了一些与当前会话失效相关的代码,以防止会话修复攻击。 只要在会话范围的bean中完成, User
实体与Principal
的绑定将在新会话中完成。
上一篇: Binding a User entity and a GlassFish Principal
下一篇: Java implementation of singular value decomposition for large sparse matrices