JSF:身份验证&授权,最好的前进方式

时间:2012-05-11 15:47:12

标签: jsf java-ee authentication jsf-2 authorization

我花了一整天的谷歌搜索并在这里查看各种问题,试图找到实施身份验证和授权的最佳解决方案。我现在想出部分解决方案,但我希望有人可以填补空白。我知道下面有很多文字,但请耐心等待:O)

背景

我继承了部分已完成的CRM应用程序,该应用程序目前使用JSF 2.0,JavaEE 6,JPA和PostgreSQL数据库。不幸的是,最初以无限智慧开始构建这个Web应用程序的人决定最好将身份验证/授权留到最后 - 我现在必须把它放进去。

应用程序基本上分为三层 - 视图,托管bean和DAO。这意味着托管bean特别“胖”,因为它们包含所有业务逻辑,验证和导航逻辑。

身份验证/授权要求

  1. 基于表单的身份验证,验证存储在PostgreSQL数据库中的凭据。
  2. 唯一可以公开访问的页面(匿名用户)将是登录页面。
  3. 我需要根据用户角色阻止访问应用程序的某些区域。例如,只有具有“管理员”角色的用户才能访问创建/编辑用户页面。
  4. 我还需要能够限制对页面某些区域的访问。例如,具有“销售代表”角色的用户应该能够查看客户详细信息,但只有在用户具有“客户服务”角色时才应显示保存/编辑按钮。
  5. 我在哪里

    我打算做的第一件事就是遵循这个User Authentication and Authorization using JAAS and Servlet 3.0 Login示例。我相信这将满足我的前3个要求。

    为了在页面上显示/隐藏保存按钮等,我可以使用this SO answer中描述的技术。这将部分解决需求4,但我认为我仍然需要保护动作方法和托管bean本身。例如,我希望能够在customer bean上的save()方法中添加注释或内容,以确保只有具有“客户服务”角色的用户才能调用它 - 这就是我开始遇到问题的地方

    我想一个选项是做类似于我在视图中建议做的事情,并使用facesContext来检查当前用户是否“在角色中”。我并不热衷于此,因为它会使我的代码混乱而宁愿使用注释。但是,如果我确实沿着这条路走下去,我将如何返回http 403状态?

    javax.annotation.security。*注释似乎非常适合对应用程序区域进行明确定义访问,但据我所知,它们只能添加到EJB中。这意味着我需要将我的所有业务逻辑从目前驻留在新EJB的托管bean中移出。我认为这可以带来额外的好处,即将业务逻辑分成它自己的一组类(代理,服务或你选择调用它们的任何东西)。这将是一个相当大的重构,但是由于缺乏单元测试或集成测试而无法帮助。我不确定访问控制的责任是否也应该在这个新的服务级别 - 我认为它应该在托管bean上。

    其他替代方案

    在我的研究中,我发现很多人都提到了Spring和Seam等框架。我对Seam的经验有限,我觉得这个项目非常适合这个项目,而且我记得我相信它解决了我所拥有的授权问题,但我认为现在推出这个问题为时已晚

    我也见过Shiro在不同的地方提到过。看过10 minute tutorial这似乎很合适,特别是与Deluan Quintao's taglib一起使用,但我找不到任何教程或如何将它与JSF Web应用程序集成的示例。

    我经常遇到的另一种选择是实施定制解决方案 - 这对我来说似乎很疯狂!

    摘要

    总而言之,我真的想要一些关于我是否在实现身份验证和授权方面走上正确道路的指导,以及我如何填写保护单个方法和/或托管bean的缺失部分(或者至少他们委托的代码)和/或我如何手动返回HTTP状态403。

2 个答案:

答案 0 :(得分:3)

在进行了大量研究之后,我得出结论:首先,我的应用程序的需求将受益于部署到完全实现Java EE规范的应用程序服务器,而不是像Tomcat这样的servlet容器。由于我正在开发的项目使用Maven,这里的关键是正确设置依赖项 - 这并不容易,并且进行了大量的谷歌搜索和反复试验:我确信有更科学的方法,可以采取。

然后我必须create a mysql module让我的应用程序正确地与数据库通信,然后删除已经实现的工厂来创建DAO并将它们转换为EJB。我还必须更新hibernate.cfg.xml以引用我添加的数据源和persistence.xml以将事务类型设置为JTA并引用JTA数据源。唯一的另一个复杂因素是正在使用Open Session In View模式,这意味着当在视图中访问实体时,我最终会遇到hibernate延迟初始化错误。我重新实现了本答案底部所示的过滤器,以解决这个问题。我认为这是一个临时的措施,可以让我重新开始工作,然后我可以有希望重构这个区域,并且无需过滤器。

转移到JBoss只花了一天时间,如果我对Java EE和Maven更有经验,我相信它可以更快地完成。现在我正处于这一点上,我能够很好地将3号缝的安全性放入项目中并利用它,而不是试图破解一个基本上是我将采取的方向的解决方案。 Seam 3的优点在于,您可以在一定程度上选择使用哪些模块,而不必添加整个框架(如Seam 2)。我认为其他一些模块也会有所帮助,但是除了其他东西之外,还可以帮助我摆脱视图模式中的开放会话。

使用Seam让我感到担心的一件事是我被告知DeltaSpike。这似乎可能会取代煤层,并且没有任何更多版本的煤层的计划。我已经决定,由于接缝仍然受到支撑,如果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() {
    }
}

答案 1 :(得分: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/

而不是使用请求过滤器或使用JAAS,Spring安全性是一个全面的安全框架,可以解决您的大部分安全问题。 您可以使用它来使用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