在我正在构建的应用程序中,我们使用直接的
Java 6 EE和JBoss(没有Spring等),使用JPA / Hibernate,JSF,CDI和EJB.
@H_403_2@我没有发现很多很好的一般安全解决方案(建议是值得欢迎的),但是我发现最好的一点是Apache Shiro.
@H_403_2@不过这似乎有一些缺点.其中有些可以在Balus C’s网站阅读:
@H_403_2@http://balusc.blogspot.com/2013/01/apache-shiro-is-it-ready-for-java-ee-6.html
@H_403_2@但是我已经偶然发现另一个大问题,已经在here中提到了关于依赖注入和代理的问题.
@H_403_2@基本上我有一个很好的基于JPA的UserDAO,它提供了身份验证所需的一切.我的数据库在persistence.xml和mydatabase-ds.xml(用于JBoss)中整齐配置.
@H_403_2@再次复制所有这个配置信息似乎很愚蠢,并将用户表查询添加到shiro.ini中.所以这就是为什么我选择编写我自己的Realm而不是使用JdbcRealm.
@H_403_2@我的第一个尝试就是授权AuthorizationRealm …类似于:
@Stateless public MyAppRealm extends AuthorizingRealm { @Inject private UserAccess userAccess; @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken userPassToken = (UsernamePasswordToken) token; User user = userAccess.getUserByEmail(userPassToken.getUsername()); if (user == null) { return null; } AuthenticationInfo info = new SimpleAuthenticationInfo(); // set data in AuthenticationInfo based on data from the user object return info; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // TODO return null; } }@H_403_2@所以这样做不好,因为MyAppRealm不能被代理,因为父类中有一个最终的init()方法. @H_403_2@我的第二个尝试是让MyAppRealm实现所有需要的接口,并将它们委托给AuthorizingRealm的实例.我不喜欢这个,但也可以尝试一下. @H_403_2@这让我进一步,webapp启动,但仍然不足.原因是在配置文件shiro.ini中,我指定了我的领域的类:
myAppRealm = com.myapp.MyAppRealm@H_403_2@这几乎告诉我,Shiro将负责创建MyAppRealm实例.因此,它不会被CDI管理,因此不会被注入,这正是我所看到的. @H_403_2@我已经看到这个SO answer,但是我看不到它可能如何工作,因为AuthorizingRealm的一个子类将继承一个最终的init()方法,这意味着该子类不能被代理. @H_403_2@有什么想法可以解决这个问题吗?
解决方法
这是一个经典问题:您有两个不同的框架,既要管理对象生命周期,又需要让它们进行交互,但都要坚持完全控制(我的心灵形象就像哥斯拉和哥打在东京市中心的战斗).您可能不会立即将Shiro视为CDI的竞争对手,但由于它创建了其对象的实例,所以它基本上包含一个微小的,基本的依赖注入框架(或许这是一个DI版本的
Greenspun’s tenth rule).我遇到了一个类似的问题,使Web框架创建并注入其支持bean的实例,与CDI进行交互.
@H_403_2@解决这个问题的方法是在两个框架之间创建一个明确的桥梁.如果真的很幸运,非CDI框架将有钩子来让您自定义对象创建,您可以在其中插入使用CDI的内容(例如,在Stripes Web框架中,您可以编写一个使用CDI的ActionResolver).
@H_403_2@如果没有,则桥必须采取代理的形式.在该代理中,您可以执行显式的CDI查找.您可以通过抓住
BeanManager
来引导进入CDI,这可以让您设置上下文,然后在其中创建bean.这样的事情
BeanManager beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager"); Bean<UserDAO> userDAObean = (Bean<UserDAO>) beanManager.resolve(beanManager.getBeans(UserDAO.class)); CreationalContext<?> creationalContext = beanManager.createCreationalContext(null); UserDAO userDAO = userDAObean.create(creationalContext);@H_403_2@userDAO是一个注入的CDI托管的bean,绑定到您现在拥有的上下文作为creationalContext. @H_403_2@当你完成了这个bean(由你自己做这个查询每次请求一次或每个应用程序生命周期一次)就可以释放这个bean:
creationalContext.release();