微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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();

原文地址:https://www.jb51.cc/java/126594.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐