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>
...
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…