Java EE – inject CDI managed beans into custom Shiro authorizationrealm
In the application I'm building, we use direct Java 6 EE and JBoss (without spring, etc.), JPA / Hibernate, JSF, CDI and EJB
I didn't find many good general security solutions (the suggestions are welcome), but I found the best one is Apache Shiro
However, this seems to have some disadvantages Some of these can be read on balus C's website:
http://balusc.blogspot.com/2013/01/apache-shiro-is-it-ready-for-java-ee-6.html
But I have stumbled upon another big problem, which has been mentioned here about dependency injection and proxy
Basically, I have a good JPA based userdao that provides everything needed for authentication My database is in persistence XML and mydatabase DS XML (for JBoss)
It seems foolish to copy all this configuration information again and add the user table query to Shiro Ini So that's why I chose to write my own realm instead of using jdbcrealm
My first attempt was to authorize the authorizationrealm... Similar to:
@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; } }
This is not good because myappream cannot be proxied because there is a final init () method in the parent class
My second attempt is to make myappream implement all required interfaces and delegate them to the instance of authoringrealm I don't like this, but I can try it
This allows me to go further, webapp starts, but it's still not enough The reason is in the configuration file Shiro Ini, I specify the class of my domain:
myAppRealm = com.myapp.MyAppRealm
This almost tells me that Shiro will be responsible for creating myappream instances Therefore, it will not be managed by CDI, so it will not be injected, which is what I see
I've seen this so answer, but I can't see how it might work, because a subclass of authorizingrealm will inherit a final init () method, which means that the subclass can't be proxied
Do you have any ideas to solve this problem?
Solution
This is a classic problem: you have two different frameworks that both manage the object life cycle and interact with them, but both must adhere to complete control (my spiritual image is like the battle between Godzilla and Goda in central Tokyo) You may not immediately see Shiro as a competitor of CDI, but because it creates instances of its objects, it basically contains a small and basic dependency injection framework (perhaps this is a di version of Greenspun's ten rule) I encountered a similar problem, which caused the web framework to create and inject instances of its supporting beans to interact with CDI
The solution to this problem is to create a clear bridge between the two frameworks If you are really lucky, the non CDI framework will have hooks to allow you to customize object creation, in which you can insert content using CDI (for example, in the strips web framework, you can write an actionresolver using CDI)
If not, the bridge must take the form of an agent In this agent, you can perform explicit CDI lookup You can boot into CDI by grasping the beanmanager, which allows you to set the context and then create beans in it Such a thing
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 is an injected CDI managed bean bound to the context you now own as creationalcontext
When you complete the bean (you can do the query by yourself, once per request or once per application life cycle), you can release the bean:
creationalContext.release();