Lau*_*ens 7 java jsf glassfish java-ee
我有一个实体类User,其中包含用户名,名字,姓氏和密码等信息,我有我的GlassFish 3.1服务器设置来执行身份验证.到现在为止还挺好.在容器对用户进行身份验证之后,我需要一些方法将主体绑定到实际的User实体.毕竟,GlassFish告诉我用户"laurens"已经过身份验证,它没有给我相应的User实体.
为此,我编写了一个JSF托管bean UserController.我想知道的是,如果这是查看实际实体的正确方法,如果有任何明显的陷阱,我没有看到.
UserController 具有以下字段:
@EJB
private UserFacade userFacade;
private User user;
Run Code Online (Sandbox Code Playgroud)
这userFacade是一个无状态会话bean,用于持久化和查找User实例.userJSF页面使用该字段来获取和设置用户的属性.
我使用以下方法执行绑定,并伴有两个辅助方法:
@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();
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
这是正确的方法吗?
谢谢!
编辑:
感谢您的投入!我考虑了一下,虽然我认为将密码移到另一个表上对我来说有点太多了,我认为我可以通过分离UserControllera @RequestScoped AuthenticationController和被剥离来解决一些问题下来@SessionScoped UserController.
该AuthenticationController会拥有emailAddress和password领域,通过网页的EMAILADDRESS和密码字段的约束.它还将包含public void authenticate()用于验证用户并随后丢弃凭证的用户.然后,@SessionScoped UserController可以绑定到适当的User实体,而无需知道密码.事实上,我相信我可以User完全删除密码字段.
您提出的方法有一些粗糙的地方,但在大多数情况下它是相当好的。
如果您打算存储对实体的引用User,那么最好在SessionScoped托管 bean 中执行此操作。这有它的优点和缺点。明显的优点是
User实体在整个应用程序流程中的所有页面上都可用。这意味着您只需在会话中将绑定Principal到实体一次。User如果需要,您可以在所有页面中重复使用绑定值。不太明显的缺点是
password字段将在内存中存储相当长的一段时间。最好的情况是,您应该在尝试身份验证后尝试使实体的密码字段无效(无论是否成功,无论该字段包含明文密码还是散列密码)。password此外,将字段定义为延迟获取 ( FetchTypeof LAZY) 而不是默认的急切获取 ( FetchTypeof )也很有意义EAGER。如果您实现这一点(特别是密码字段的无效),您将需要注意涉及对实体进行合并操作的问题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;
}
Run Code Online (Sandbox Code Playgroud)
这是从 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>
Run Code Online (Sandbox Code Playgroud)
请注意在身份验证成功的情况下使用 POST-REDIRECT-GET 模式。我留下了一些与重定向前当前会话失效相关的代码,以防止会话固定攻击。User实体到的绑定Principal将在新会话中完成,只要它是在会话范围 bean 中完成的。
| 归档时间: |
|
| 查看次数: |
2224 次 |
| 最近记录: |