JSF:身份验证和授权,最好的前进方式
Posted
技术标签:
【中文标题】JSF:身份验证和授权,最好的前进方式【英文标题】:JSF: Authentication & Authorization, best way forward 【发布时间】:2012-05-20 05:52:23 【问题描述】:我整天都在谷歌上搜索并查看这里的各种问题,试图提出实现身份验证和授权的最佳解决方案。我现在已经提出了部分解决方案,但我希望有人可以填补空白。我知道下面有很多文字,但请多多包涵:O)
背景
我继承了一个部分完成的 CRM 应用程序,该应用程序目前使用 JSF 2.0、JavaEE 6、JPA 和 PostgreSQL 数据库。不幸的是,最初以无限智慧开始构建这个网络应用程序的人决定最好将身份验证/授权留到最后——我现在必须把它放进去。
应用程序本质上分为三层 - 视图、托管 bean 和 DAO。这意味着托管 bean 特别“胖”,因为它们包含所有业务逻辑、验证和导航逻辑。
身份验证/授权要求
-
基于表单的身份验证,根据存储在 PostgreSQL 数据库中的凭据进行验证。
唯一可以公开访问(匿名用户)的页面是登录页面。
我需要根据用户角色阻止访问应用程序的某些区域。例如,只有具有“管理员”角色的用户才能访问创建/编辑用户页面。
我还需要能够限制对页面某些区域的访问。例如,具有“销售代表”角色的用户应该能够查看客户详细信息,但只有当用户具有“客户服务”角色时才会显示保存/编辑按钮。
我在哪里
我计划做的第一件事是遵循这个User Authentication and Authorization using JAAS and Servlet 3.0 Login 示例。我相信这将满足我的前 3 个要求。
为了在页面上显示/隐藏保存按钮等,我可以使用this SO answer 中描述的技术。这将部分解决需求 4,但是我认为我仍然需要保护操作方法和/或托管 bean 本身。例如,我希望能够在客户 bean 上的 save() 方法中添加注释或其他内容,以确保只有具有“客户服务”角色的用户才能调用它——这就是我开始遇到问题的地方.
我想一种选择是做一些类似于我在视图中建议做的事情,并使用 facesContext 检查当前用户是否“在角色中”。我对此并不热衷,因为它只会弄乱我的代码,而宁愿使用注释。但是,如果我确实走这条路,我将如何返回 http 403 状态?
javax.annotation.security.* 注释似乎非常适合以声明方式定义对应用程序区域的访问,但据我所知,它们只能添加到 EJB 中。这意味着我需要将我的所有业务逻辑从它当前所在的托管 bean 中移到新的 EJB 中。我认为这将具有将业务逻辑分离到它自己的一组类(委托、服务或您选择调用它们的任何内容)中的额外好处。这将是一个相当大的重构,但是不会因为缺少单元测试或集成测试而有所帮助。我不确定访问控制的责任是否应该在这个新的服务级别上——我认为它应该在托管 bean 上。
其他选择
在我的研究中,我发现很多人提到了 Spring 和 Seam 等框架。我对 Seam 的经验有限,我认为它非常适合这个项目,据我回忆,我相信它解决了我遇到的授权问题,但我认为现在介绍它为时已晚.
我还看到很多地方都提到了Shiro。看过 10 minute tutorial 之后,这似乎很合适,尤其是与 Deluan Quintao's taglib 结合使用时,但我找不到任何关于如何将其与 JSF Web 应用程序集成的教程或示例。
我经常意外地遇到的另一种选择是实施自定义解决方案 - 这对我来说似乎很疯狂!
总结
总之,我真的很想得到一些指导,了解我在实现身份验证和授权方面是否走在正确的道路上,以及我如何填补保护单个方法和/或托管 bean(或至少是他们委托给的代码)和/或我如何手动返回 HTTP 状态 403。
【问题讨论】:
>我找不到任何关于如何将其与 JSF Web 应用程序集成的教程或示例。 - 现在有一个,但仍然不理想:balusc.blogspot.com/2013/01/… 【参考方案1】:经过大量研究,我得出的结论是,首先我的应用程序的需求将受益于部署到完全实现 Java EE 规范的应用程序服务器,而不是像 Tomcat 这样的 servlet 容器。由于我正在从事的项目使用 Maven,因此这里的关键是正确设置依赖项 - 这并不容易,并且需要进行相当多的谷歌搜索和反复试验:我相信有一种更科学的方法可以带走。
然后我不得不create a mysql module 让我的应用程序正确地与数据库通信,然后删除已实现的工厂以创建 DAO 并将它们转换为 EJB。我还必须更新 hibernate.cfg.xml 以引用我添加的数据源和 persistence.xml 将事务类型设置为 JTA 并引用 JTA 数据源。唯一的其他复杂情况是正在使用 Open Session In View 模式,这意味着当在视图中访问实体时,我最终会遇到休眠延迟初始化错误。我重新实现了这个答案底部所示的过滤器,以解决这个问题。我认为这是一种临时措施,可以在我希望重构该区域并消除对过滤器的需求之前让事情再次正常运行。
迁移到 JBoss 只用了一天多的时间,如果我对 Java EE 和 Maven 有更多经验,我相信它可以做得更快。既然我已经到了那个地步,我就可以将接缝 3 安全性放到项目中并利用它,而不是试图拼凑一个解决方案,这基本上是我将要采取的方向。 Seam 3 的好处在于,您可以在一定程度上挑选您使用的模块,而不必添加整个框架(如 Seam 2)。我认为其他一些模块也会有所帮助,并且将帮助我摆脱视图模式中的打开会话。
在使用 Seam 时确实让我担心的一件事是有人告诉我 DeltaSpike。这看起来好像它可能会取代 seam 并且没有任何更多版本的 seam 的计划。我已经决定,既然接缝仍然受到支持,并且如果 DeltaSpike 需要与接缝 3 一样长的时间来实现,那么使用接缝 3 是非常安全的。
我希望能够写一篇适当的博文,详细描述这次迁移。
public class OSVRequestFilter implements Filter
private static final String UserTransaction = "java:comp/UserTransaction";
private static Logger logger = LoggerFactory.getLogger(EntityManagerRequestFilter.class);
public void init(FilterConfig config) throws ServletException
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
if (request instanceof HttpServletRequest)
doFilter(request, response, chain, getUserTransaction());
private UserTransaction getUserTransaction() throws ServletException
try
Context ctx = new InitialContext();
return (UserTransaction)PortableRemoteObject.narrow(ctx.lookup(UserTransaction), UserTransaction.class);
catch (NamingException ex)
logger.error("Failed to get " + UserTransaction, ex);
throw new ServletException(ex);
private void doFilter(ServletRequest request, ServletResponse response, FilterChain chain, UserTransaction utx) throws IOException, ServletException
try
utx.begin();
chain.doFilter(request, response);
if (utx.getStatus() == Status.STATUS_ACTIVE)
utx.commit();
else
utx.rollback();
catch (ServletException ex)
onError(utx);
throw ex;
catch (IOException ex)
onError(utx);
throw ex;
catch (RuntimeException ex)
onError(utx);
throw ex;
catch (Throwable ex)
onError(utx);
throw new ServletException(ex);
private void onError(UserTransaction utx) throws IOException, ServletException
try
if ((utx != null) && (utx.getStatus() == Status.STATUS_ACTIVE))
utx.rollback();
catch (Throwable e1)
logger.error("Cannot rollback transaction", e1);
public void destroy()
【讨论】:
【参考方案2】:您是否尝试过使用 Spring Security - 最新版本 3
http://janistoolbox.typepad.com/blog/2010/03/j2ee-security-java-serverfaces-jsf-spring-security.html
http://ocpsoft.org/java/jsf-java/spring-security-what-happens-after-you-log-in/
spring security 不是使用请求过滤器或使用 JAAS,而是一个全面的安全框架,可以解决您的大部分安全问题。 您可以使用它使用 db 领域对用户进行身份验证,根据提供的身份验证信息授权他并根据需要重定向。
您可以保护您编写的方法 http://blog.solidcraft.eu/2011/03/spring-security-by-example-securing.html
@PreAuthorize("hasRole('ROLE_XXX')") 是这样的
使页面的某些元素安全.. //内容
更多阅读和示例 http://static.springsource.org/spring-security/site/petclinic-tutorial.html
【讨论】:
感谢您的回答。我有点听从你的建议,但有点扭曲 - 我将使用接缝安全代替:O)以上是关于JSF:身份验证和授权,最好的前进方式的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security 3.1.4 taglib 授权/身份验证不适用于 Tomcat 7 上 JSF 2.2 中的角色层次结构