我该如何在JSF中实现安全性?

16

我正在使用Java EE 6和所有参考实现。已经为一些页面设置了安全约束,例如在/secure/*下的所有内容都需要进行安全控制。这是粗粒度的安全。如果两个用户都有相同的角色,但是同一页面的某些内容只应该对用户"John"可见呢?或者完全不同的页面应该显示给"John"?我有许多未解答的问题,所以如果有人能提供一些链接/解释或涵盖此内容的书籍,那将是很好的。我需要更细粒度的安全控制。


我是否正确地阅读了评论,赏金已经承诺了吗? - Matt Handy
@MattHandy 我会给最佳答案提供赏金。我非常感兴趣如何在JSF中实现更细粒度的安全控制,例如如何隐藏那些没有特定角色、身份等的内容。还有后端如何实现。 - LuckyLuke
请阅读我和Viper进行了一个小时的讨论,我在其中解释了很多事情。 - Anonymoose
你是否考虑过使用外部模块/框架(如Spring Security),或者这不是一个选项? - quantum
4个回答

25
您希望的细粒度安全功能不仅存在,甚至Oracle还有一篇详细介绍该主题的有用博客文章,其中包含示例代码。
因为简单地链接文档并离开会显得简短和不礼貌,所以接下来我会谈一些我理解的最佳组合方式。
第0个问题:粗粒度、声明式安全。 声明式安全最大的问题在于它强制你在设计时迭代定义所有用户角色。这有两个极其不利的原因:首先,它无法将你的安全模型充分抽象出来(无法充分未来证明你的应用程序,并打开信息泄露漏洞的门),其次,它将你的用户角色与应用程序的直接设计联系起来,通常不能在需要或必要时提供细粒度权限或ACLs
实际上,这是一个缺乏抽象的问题。你正在使用一个立即满足你当前需求的系统,但不能指望它在应用程序的生命周期内可操作或可维护,因为角色变得更加复杂,代码库的复杂性稳步增加。

使用托管Bean进行细粒度安全

这里的一级解决方案是使用抽象模型,允许您在每个JSF方法调用的上下文中独立定义用户角色,以便根据需要进行交换。作为奖励,这使您能够定义更细粒度的权限,因为这种方案允许您按方法而不是按视图、端点或bean定义权限。如果角色发生变化?您只需在单个位置更新权限模型,而不是去每个bean并交换其用户定义。 aforelinked article 比我愿意涵盖的要详细得多,因此我强烈建议阅读博客文章。但是,重点是,要正确地做到这一点,您应该提供身份验证堆栈和详细说明权限角色的注释层,并且只有在明确和有意连接两者的地方才会相遇。

本文未涉及细粒度方法调用和合理的安全策略定义,这部分需要读者自己思考。如果您在这方面有问题,请随意在评论区或后续问题中提问,因为这些问题对广大读者都有用。

改进

可能这个解决方案并不足够强大以满足您的需求。例如,如果您希望使用LDAPKerberos认证用户,以提供用户和角色的统一表示,那么这只能提供部分解决方案。在这个领域中有许多great resources可供参考,但其余部分需要读者自行思考。

在完美的世界中,你应该这样定义你的应用程序安全性。你的需求可能会有所不同,对于小规模的东西,简单而声明性的安全性可能就足够满足你的需求。毕竟,这就是它继续存在的原因。
但是,对于需要安全、正确地满足大量用户需求的大型应用程序来说,这是正确的方法。虽然需要更多的知识和开销,但如果你一开始就做得正确,它将为你节省大量时间、精力和挫折。
祝你的应用程序好运。

10

最简单的方法是

<h:panelGroup rendered="#{request.userPrincipal.name == 'user1'}">
    <p>Content for user 1</p>
</h:panelGroup>
<h:panelGroup rendered="#{request.userPrincipal.name == 'user2'}">
    <p>Content for user 2</p>
</h:panelGroup>

1
关于这个问题:我有一个名为profile.xhtml的页面和一个名为username的请求参数。如果用户访问profile.xhtml?username=Amanda,并且没有以Amanda的身份登录,则会看到受限制的个人资料视图。现在,您会怎么做呢?是将这些检查代码放在视图代码中,还是为该用户提供另一个视图?比如说你有两个profile.xhtml页面。或者......你创建了一个profile.xhtml页面,然后使用Facelets标记(组合)根据用户的登录身份包含个人资料视图的内容?解决这类问题的选项有哪些? - LuckyLuke
这就是为什么我不喜欢Facelets的原因之一。将视图关注点与控制器关注点混淆,使得简单的事情变得容易,而中级的事情变得非常困难。我可能最终会将两个页面粘在一起作为一个facelet,每个部分都由渲染属性保护。有没有更好的方法? - Anonymoose
有其他的选择吗?当使用容器管理的安全性时,我使用“form”登录并发布到j_security。在哪里初始化一个包含当前登录用户信息的“UserBean”? - LuckyLuke
1
只需将UserPrincipal注入您的bean中,就像魔术一样!所有身份验证/授权业务都由容器处理。 - Anonymoose
不,因为我会给最佳答案的人450个声望,关于如何在JSF中实现更细粒度的安全性?你能来Java聊天室一下吗? - LuckyLuke
显示剩余3条评论

4
展示同一页面的多个版本并不涉及安全性(不同版本≠链接隐藏),限制访问并需要额外的授权才是。如果您不介意,我会回答这个问题。
您可以阅读有关身份验证和授权的信息on JAAS page。在我看来,这也是最好的框架。它需要一些时间来掌握,但之后,它相当容易,你甚至会意识到它根本不是重量级的 - 你不被强制使用每个功能。(就像EJB一样)
JAAS甚至可以记录您使用ldap或Windows帐户,甚至支持多个身份验证步骤 - 您可以实现密码+短信登录。当然,您也可以使用acegi,只是不那么容易。
由于您已经提到了JSF,因此JAAS比acegi更适合,您可以使用@RolesAllowed注释任何后端bean,如果用户会话不满足要求,则会抛出SecurityException。这适用于servlet和bean(ejb,backing),但不适用于jsps(无论如何也没有太多意义)。
您可以在这里@RolesAllowed阅读相关信息,但是如果您已经考虑它,请不要错过JBoss Seam Security - 它建立在安全注释和JAAS之上,而且使用起来也相当令人上瘾。值得一读。
顺便说一句,各位:我并不是为了声望而发帖,只是发现了有趣的问题,所以...请随意争取悬赏 :)

1
通常这种内容将保存在会话变量中,因此您不必考虑哪个用户已登录。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接