Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to @Inject my DAO in a Custom Apache Shiro AuthorizingRealm

Tags:

java

jsf

cdi

shiro

I'm trying to inject my UserDAO inside my custom AuthorizingRealm that Apache Shiro is using but... I get null.

What am I doing wrong?

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;
        }
    }

}

What I must do to allow CDI to be aware of the @Inject inside my custom realm and inject my UserDAO properly?

like image 628
BBacon Avatar asked Dec 21 '22 08:12

BBacon


1 Answers

The default EnvironmentLoaderListener used by Apache Shiro is not CDI aware. The solution is to build one that is and replace the original reference in the web.xml to point for your customized one.

Note: CDI injection is supported in listeners automatically, but the listeners must request beans via CDI mechanism. The custom listener will use @Inject to request beans and will create JpaRealm as CDI bean, which will have all dependencies injected. The default Shire listener would not create JpaRealm as a CDI-enabled bean via @Inject.

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>
...
like image 97
BBacon Avatar answered Feb 13 '23 03:02

BBacon