【问题标题】:Unable to @Inject my DAO in a Custom Apache Shiro AuthorizingRealm无法在自定义 Apache Shiro AuthorizingRealm 中 @Inject 我的 DAO
【发布时间】:2013-03-14 08:35:02
【问题描述】:

我正在尝试将我的 UserDAO 注入到 Apache Shiro 正在使用的自定义 AuthorizingRealm 中,但是......我得到了 null。

我做错了什么?

shiro.ini

[main]
user = demo.shiro.security.FacesAjaxAwareUserFilter
realmA = demo.shiro.security.JpaRealm
credentialsMatcher = org.apache.shiro.authc.credential.SimpleCredentialsMatcher
realmA.credentialsMatcher = $credentialsMatcher
securityManager.realms = $realmA
user.loginUrl = /pages/public/login.xhtml

[users]
admin = admin
user = user

[urls]
# public files and folders
/index.html = anon
/resources/** = anon
/pages/public/** = anon

# restricted files and folders
/pages/admin/** = user
/pages/user/** = user

JpaRealm.java

public class JpaRealm extends AuthorizingRealm {

    @Inject
    private UserDao userDao;

    public JpaRealm() {
        setCredentialsMatcher(new Sha256CredentialsMatcher());
    }

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authToken;
        User user = userDao.getForUsername(token.getUsername());
        if (user != null) {
            return new SimpleAuthenticationInfo(user.getId(), user.getPassword(), getName());
        } else {
            return null;
        }
    }

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Long userId = (Long) principals.fromRealm(getName()).iterator().next();
        User user = userDao.findByKey(userId);
        if (user != null) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            for (Role role : user.getRoles()) {
                info.addRole(role.getDescription());
                for (Permition permition : role.getPermitions()) {
                    info.addStringPermission(permition.getDescription());
                }
            }
            return info;
        } else {
            return null;
        }
    }

}

我必须做些什么才能让 CDI 了解我的自定义领域中的 @Inject 并正确注入我的 UserDAO?

【问题讨论】:

    标签: java jsf cdi shiro


    【解决方案1】:

    Apache Shiro 使用的默认 EnvironmentLoaderListener 不支持 CDI。 解决方案是构建一个,并将 web.xml 中的原始引用替换为指向您自定义的引用。

    注意:listeners automatically 支持 CDI 注入,但侦听器必须通过 CDI 机制请求 bean。自定义侦听器将使用@Inject 请求bean,并将创建JpaRealm 作为CDI bean,它将注入所有依赖项。默认 Shire 侦听器不会通过 @Inject 创建 JpaRealm 作为启用 CDI 的 bean。

    CustomCredentialsMatcher.java

    public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
    }
    

    CustomEnvironmentLoaderListener.java

    public class CustomEnvironmentLoaderListener extends EnvironmentLoaderListener {
    
        @Inject
        private JpaRealm jpaRealm;
    
        @Override
        protected WebEnvironment createEnvironment(ServletContext pServletContext) {
            WebEnvironment environment = super.createEnvironment(pServletContext);
            RealmSecurityManager rsm = (RealmSecurityManager) environment.getSecurityManager();
            PasswordService passwordService = new DefaultPasswordService();
            PasswordMatcher passwordMatcher = new PasswordMatcher();
            passwordMatcher.setPasswordService(passwordService);
            jpaRealm.setCredentialsMatcher(passwordMatcher);
            rsm.setRealm(jpaRealm);
            ((DefaultWebEnvironment) environment).setSecurityManager(rsm);
            return environment;
        }
    
    }
    

    FacesAjaxAwareUserFilter.java

    public class FacesAjaxAwareUserFilter extends UserFilter {
    
        private static final String FACES_REDIRECT_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><partial-response><redirect url=\"%s\"></redirect></partial-response>";
    
        @Override
        protected void redirectToLogin(ServletRequest req, ServletResponse res) throws IOException {
            HttpServletRequest request = (HttpServletRequest) req;
    
            if ("partial/ajax".equals(request.getHeader("Faces-Request"))) {
                res.setContentType("text/xml");
                res.setCharacterEncoding("UTF-8");
                res.getWriter().printf(FACES_REDIRECT_XML, request.getContextPath() + getLoginUrl());
            } else {
                super.redirectToLogin(req, res);
            }
        }
    
    }
    

    JpaRealm.java

    public class JpaRealm extends AuthorizingRealm {
    
        private static String REALM_NAME = "jpaRealm";
    
        @Inject
        private UserDao userDao;
    
        @Inject
        private RoleDao roleDao;
    
        @Inject
        private PermissionDao permissionDao;
    
        public JpaRealm() {
            setName(REALM_NAME); // This name must match the name in the User class's getPrincipals() method
        }
    
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
            UsernamePasswordToken token = (UsernamePasswordToken) authToken;
            User user = userDao.getForUsername(token.getUsername());
            if (user != null) {
                return new SimpleAuthenticationInfo(user.getId(), user.getPassword(), getName());
            } else {
                return null;
            }
        }
    
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            Long userId = (Long) principals.fromRealm(getName()).iterator().next();
            User user = userDao.findByKey(userId);
            if (user != null) {
                SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
                for (Role role : roleDao.getForUser(user)) {
                    info.addRole(role.getDescription());
                    for (Permition permition : permissionDao.getForRole(role)) {
                        info.addStringPermission(permition.getDescription());
                    }
                }
                return info;
            } else {
                return null;
            }
        }
    
    }
    

    shiro.ini

    [main]
    user = com.boss.mrfoods.security.FacesAjaxAwareUserFilter
    user.loginUrl = /pages/public/login.xhtml
    
    [urls]
    /index.html = anon
    /pages/index.xhtml = anon
    /pages/public/** = anon
    
    /pages/admin/** = user, roles[ADMIN]
    /pages/user/** = user, roles[USER]
    

    web.xml

    ...
    
    <listener>
        <listener-class>com.boss.mrfoods.security.CustomEnvironmentLoaderListener</listener-class>
    </listener>
    
    <filter>
        <filter-name>ShiroFilter</filter-name>
        <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ShiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    ...
    

    【讨论】:

    • 优秀的答案!但我有一个问题:是什么让这个自定义 EnvironmentLoaderListener “CDI 感知”?注入注释?这个监听器实现是否使 shiro.ini conf 过时了?因为我们在线设置了一个固定的realm:rsm.setRealm(jpaRealm);
    • 我包含了更多代码来回答“CDI 感知”部分(请参阅 JpaRealm.java)。不知何故 shiro.ini 并没有过时......你需要它来定义一些配置......删除它会导致很多问题。如果你愿意构建一个 shiro.ini 的免费安全,我认为那将是非常好的。
    • 问个问题,为什么要从 shiro.ini 中删除 realmA ? (或者只是为了简短?)。 tnx
    • 我在答案中添加了一些关于 CDI 感知的解释:侦听器自动支持 CDI 注入,但侦听器必须通过 CDI 机制请求 bean。自定义侦听器将使用@Inject 请求bean 并将创建JpaRealm 作为CDI bean,它将注入所有依赖项。默认 Shire 侦听器不会通过 @Inject 创建 JpaRealm 作为启用 CDI 的 bean。
    【解决方案2】:

    jpaRealm 在“CustomEnvironmentLoaderListener”中恰好为空。我也尝试在 JpaRealm 类上设置 @Service("JpaRealm") 注释,以便容器可能知道注入但仍然没有运气。无论哪种情况,JpaRealm 都是 null。

    如果我们在自定义领域中进行注入工作,还需要做什么。

    【讨论】:

    • 我编辑了我的 aswer 以包含更多来自我的软件的代码。如果还不够,请告诉我。如果我迟到了 15 天,我很抱歉。
    【解决方案3】:

    使用 ShiroWebModule 注入您的自定义领域

    public class PocShiroModule extends ShiroWebModule {
    
        public PocShiroModule(ServletContext servletContext) {
            super(servletContext);
        }
    
        @Override
        protected void configureShiroWeb() {
            bindConstant().annotatedWith(Names.named("shiro.globalSessionTimeout"))
                    .to(30000L);
            bind(Realm.class).to(JPARealm.class);
        }
    
        @Provides
        @Singleton
        Set<Realm> provideRealmSet(Realm realm) {
            Set<Realm> result = new HashSet<Realm>();
            result.add(realm);
            return result;
        }
    
    }
    

    在你的上下文监听器中注册这个模块

    public class PocGuiceServletConfig extends GuiceServletContextListener {
    
        private ServletContext context = null;
    
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            this.context = servletContextEvent.getServletContext();
            super.contextInitialized(servletContextEvent);
        }
    
        @Override
        protected synchronized Injector getInjector() {
            return Guice.createInjector(
                    new PocModule(), 
                    new PocShiroModule(context), 
                    new ShiroAopModule());
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-09-01
      • 2017-11-24
      • 2016-10-29
      • 2012-04-13
      • 2019-01-04
      • 2014-04-07
      • 2016-12-04
      • 2018-04-03
      • 2016-03-28
      相关资源
      最近更新 更多