2013-03-24 64 views
3

我试图将我的UserDAO注入到Apache Shiro正在使用的自定义AuthorizingRealm中,但是...我得到空值。无法在自定义Apache Shiro中注入我的DAO AuthorizingRealm

我在做什么错?

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?

回答

11

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

注意: CDI注入支持listeners automatically,但听众必须通过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> 
... 
+1

出色答卷!但我有一个问题:是什么让这个自定义EnvironmentLoaderListener“CDI意识到”?注入注释?这个监听器实现是否使shiro.ini conf过时了?因为我们正在设定一个固定的领域: rsm。setRealm(jpaRealm); – salihcenap 2013-07-23 10:57:22

+0

我包含了更多的代码来回答“CDI意识”部分(请参阅JpaRealm.java)。不知怎的,shiro.ini并没有被废弃......你需要它来定义一些配置......删除它会导致很多问题。如果你打算建立一个shiro.ini免费安全,我认为这将是非常好的。 – MBarni 2013-08-01 12:33:01

+0

Juste一个问题,你为什么从你的shiro.ini中删除realmA? (或只是为了简洁?)。 tnx – mboullouz 2015-04-11 21:23:47

0

jpaRealm是碰巧在 'CustomEnvironmentLoaderListener' 空。我试着在JpaRealm类上设置@Service(“JpaRealm”)注释,以便容器可以知道注入,但仍然没有运气。无论哪种情况,JpaRealm均为null。

还有什么是必要的,以防止在自定义领域工作注射。

+0

我编辑了我的aswer以包含来自我的软件的更多代码。如果它不够让我知道。如果我迟了15天,我很抱歉。 – MBarni 2013-08-01 12:29:54

0

使用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()); 
    } 
}