我想知道当前的方法是如何使用JSF2.0(如果存在任何组件)和Java 6核心机制(登录/检查权限/注销),并将用户信息保存在JPA实体中的web应用程序的用户身份验证。Oracle Java教程在这方面有点稀疏(只处理servlet)。
这是,没有使用整个其他框架,比如Security (acegi)或Seam,但如果可能的话,可以尝试使用新的Java 6平台( whole )。
发布于 2010-06-08 12:32:51
在搜索Web并尝试了许多不同的方法之后,下面是我对Java 6身份验证的建议:
设置安全域:
在我的例子中,我的数据库中有用户。因此,我按照这篇博客文章创建了一个JDBC领域,它可以根据我的数据库表中的用户名和MD5哈希密码对用户进行身份验证:
http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html
注意:文章谈到了数据库中的一个用户和一个组表。我有一个带有UserType enum属性的用户类,它通过javax.persistence注释映射到数据库。我为用户和组配置了相同的表,使用userType列作为组列,运行良好。
使用表单认证:
仍然按照上面的博客文章,配置您的web.xml和sun-web.xml,但是不要使用基本身份验证,而是使用表单(实际上,您使用的表单并不重要,但我最终使用了表单)。使用标准HTML,而不是JSF。
然后使用BalusC上面关于延迟从数据库初始化用户信息的技巧。他建议在托管bean中从faces上下文中获取主体。我使用了一个有状态会话bean来存储每个用户的会话信息,因此我注入了会话上下文:
@Resource
private SessionContext sessionContext;使用主体,我可以检查用户名,并使用Entity从数据库中获取用户信息并存储在我的SessionInformation EJB中。
注销:
我还四处寻找最好的注销方式。我发现最好的方法是使用Servlet:
@WebServlet(name = "LogoutServlet", urlPatterns = {"/logout"})
public class LogoutServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession(false);
// Destroys the session for this user.
if (session != null)
session.invalidate();
// Redirects back to the initial page.
response.sendRedirect(request.getContextPath());
}
}虽然考虑到这个问题的日期,我的回答真的很晚了,但我希望这能帮助其他从谷歌来到这里的人,就像我一样。
再见,
Vítor Souza
发布于 2010-02-05 12:31:33
我想您希望基于表单的认证使用部署描述符和j_security_check。
您也可以在JSF中使用与教程中演示的相同的预先定义的字段名j_username和j_password来实现这一点。
例如。
<form action="j_security_check" method="post">
<h:outputLabel for="j_username" value="Username" />
<h:inputText id="j_username" />
<br />
<h:outputLabel for="j_password" value="Password" />
<h:inputSecret id="j_password" />
<br />
<h:commandButton value="Login" />
</form>您可以在User getter中执行延迟加载,以检查User是否已经登录,如果没有,则检查请求中是否存在Principal,如果存在,则获取与j_username关联的User。
package com.stackoverflow.q2206911;
import java.io.IOException;
import java.security.Principal;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
@ManagedBean
@SessionScoped
public class Auth {
private User user; // The JPA entity.
@EJB
private UserService userService;
public User getUser() {
if (user == null) {
Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
if (principal != null) {
user = userService.find(principal.getName()); // Find User by j_username.
}
}
return user;
}
}在JSF中,User显然可以通过#{auth.user}访问。
若要注销,请执行HttpServletRequest#logout() (并将User设置为null!)。您可以通过HttpServletRequest通过ExternalContext#getRequest()获得JSF中的句柄。您也可以完全使会话无效。
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "login?faces-redirect=true";
}对于剩余部分(在部署描述符和领域中定义用户、角色和约束),只需按照通常的方式遵循Java 6教程和remnant容器文档。
Update:您还可以使用新的Servlet3.0 HttpServletRequest#login()进行编程登录,而不是使用j_security_check,因为在某些服务器容器中,调度程序可能无法访问j_security_check。在本例中,您可以使用一个具有username和password属性的完整JSF表单和一个bean,以及一个类似于以下内容的login方法:
<h:form>
<h:outputLabel for="username" value="Username" />
<h:inputText id="username" value="#{auth.username}" required="true" />
<h:message for="username" />
<br />
<h:outputLabel for="password" value="Password" />
<h:inputSecret id="password" value="#{auth.password}" required="true" />
<h:message for="password" />
<br />
<h:commandButton value="Login" action="#{auth.login}" />
<h:messages globalOnly="true" />
</h:form>并且这个视图限定了托管bean的作用域,它还记得最初请求的页面:
@ManagedBean
@ViewScoped
public class Auth {
private String username;
private String password;
private String originalURL;
@PostConstruct
public void init() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
originalURL = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
if (originalURL == null) {
originalURL = externalContext.getRequestContextPath() + "/home.xhtml";
} else {
String originalQuery = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_QUERY_STRING);
if (originalQuery != null) {
originalURL += "?" + originalQuery;
}
}
}
@EJB
private UserService userService;
public void login() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
try {
request.login(username, password);
User user = userService.find(username, password);
externalContext.getSessionMap().put("user", user);
externalContext.redirect(originalURL);
} catch (ServletException e) {
// Handle unknown username/password in request.login().
context.addMessage(null, new FacesMessage("Unknown login"));
}
}
public void logout() throws IOException {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
externalContext.invalidateSession();
externalContext.redirect(externalContext.getRequestContextPath() + "/login.xhtml");
}
// Getters/setters for username and password.
}这样,User就可以通过#{user}在JSF中访问。
发布于 2012-11-05 11:04:04
应该指出,将身份验证问题完全留给前端控制器(例如Apache服务器)并计算HttpServletRequest.getRemoteUser()是一种选择,后者是REMOTE_USER环境变量的JAVA表示。这也允许复杂的登录设计,如Shibboleth认证。通过web服务器过滤对servlet容器的请求对于生产环境来说是一个很好的设计,通常使用mod_jk进行过滤。
https://stackoverflow.com/questions/2206911
复制相似问题