java-ee – 在自定义Shiro AuthorizationRealm中注入CDI托管bean

在我正在构建的应用程序中,我们使用直接的 Java 6 EE和JBoss(没有Spring等),使用JPA / Hibernate,JSF,CDI和EJB.

我没有发现很多很好的一般安全解决方案(建议是值得欢迎的),但是我发现最好的一点是Apache Shiro.

不过这似乎有一些缺点.其中有些可以在Balus C’s网站阅读:

http://balusc.blogspot.com/2013/01/apache-shiro-is-it-ready-for-java-ee-6.html

但是我已经偶然发现另一个大问题,已经在here中提到了关于依赖注入和代理的问题.

基本上我有一个很好的基于JPA的UserDAO,它提供了身份验证所需的一切.我的数据库在persistence.xml和mydatabase-ds.xml(用于JBoss)中整齐配置.

再次复制所有这个配置信息似乎很愚蠢,并将用户查询添加到shiro.ini中.所以这就是为什么我选择编写我自己的Realm而不是使用JdbcRealm.

我的第一个尝试就是授权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;
    }
}

所以这样做不好,因为MyAppRealm不能被代理,因为父类中有一个最终的init()方法.

我的第二个尝试是让MyAppRealm实现所有需要的接口,并将它们委托给AuthorizingRealm的实例.我不喜欢这个,但也可以尝试一下.

这让我进一步,webapp启动,但仍然不足.原因是在配置文件shiro.ini中,我指定了我的领域的类:

myAppRealm = com.myapp.MyAppRealm

这几乎告诉我,Shiro将负责创建MyAppRealm实例.因此,它不会被CDI管理,因此不会被注入,这正是我所看到的.

我已经看到这个SO answer,但是我看不到它可能如何工作,因为AuthorizingRealm的一个子类将继承一个最终的init()方法,这意味着该子类不能被代理.

有什么想法可以解决这个问题吗?

解决方法

这是一个经典问题:您有两个不同的框架,既要管理对象生命周期,又需要让它们进行交互,但都要坚持完全控制(我的心灵形象就像哥斯拉和哥打在东京市中心的战斗).您可能不会立即将Shiro视为CDI的竞争对手,但由于它创建了其对象的实例,所以它基本上包含一个微小的,基本的依赖注入框架(或许这是一个DI版本的 Greenspun’s tenth rule).我遇到了一个类似的问题,使Web框架创建并注入其支持bean的实例,与CDI进行交互.

解决这个问题的方法是在两个框架之间创建一个明确的桥梁.如果真的很幸运,非CDI框架将有钩子来让您自定义对象创建,您可以在其中插入使用CDI的内容(例如,在Stripes Web框架中,您可以编写一个使用CDI的ActionResolver).

如果没有,则桥必须采取代理的形式.在该代理中,您可以执行显式的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);

userDAO是一个注入的CDI托管的bean,绑定到您现在拥有的上下文作为creationalContext.

当你完成了这个bean(由你自己做这个查询每次请求一次或每个应用程序生命周期一次)就可以释放这个bean:

creationalContext.release();

相关文章

最近看了一下学习资料,感觉进制转换其实还是挺有意思的,尤...
/*HashSet 基本操作 * --set:元素是无序的,存入和取出顺序不...
/*list 基本操作 * * List a=new List(); * 增 * a.add(inde...
/* * 内部类 * */ 1 class OutClass{ 2 //定义外部类的成员变...
集合的操作Iterator、Collection、Set和HashSet关系Iterator...
接口中常量的修饰关键字:public,static,final(常量)函数...