I'm creating a rest api using Jersey 2.5.1. I'm using HK2 for dependency injection. Later on I decided to use Apache Shiro for authentication and authorization.
While creating my own custom Shiro Realm I ran into some problems. In my realm I wanted to inject a dependency. However, when I ran my application the dependency was not resolved.
Here is my setup:
web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>my.app.api.MyApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyApplication</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<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>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
shiro.ini
[main]
authcBasicRealm = my.app.api.MyCustomRealm
matcher = my.app.api.MyCustomCredentialsMatcher
authcBasicRealm.credentialsMatcher = $matcher
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $cacheManager
[urls]
/** = authcBasic
MyApplication.java
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new ApplicationBinder());
packages(true, "my.app.api");
}
}
ApplicationBinder.java
public class ApplicationBinder extends AbstractBinder {
@Override
protected void configure() {
bind(UserDAO.class).to(new TypeLiteral<Dao<User>>(){});
bind(RealDatasource.class).to(DataSource.class);
}
}
MyCustomRealm.java
public class MyCustomRealm extends JdbcRealm {
@Inject DataSource source;
public MyCustomRealm() {
super();
}
@PostConstruct
private void postConstruct() {
// postConstruct is never executed
setDataSource(source);
}
}
So, the problem is that source is not injected in MyCustomRealm. All other classes that isn't created by Shiro gets its dependencies injected.
Could the problem be that Shiro is creating my CustomRealm via the ini file?
I ran into a similar issue, and, while this is probably no longer an issue for you, I wanted to provide the work-around I used.
The issue is ownership of MyCustomRealm
. It is being created by shiro in the org.apache.shiro.web.env.EnvironmentLoaderListener
by reading the ini file which is outside the scope of the hk2 provider in the Jersey servlet.
Dependency injection is only done when the object is being provided by hk2's ServiceLocator--shiro has no knowledge of this locator and only constructs an instance of MyCustomRealm
with its default constructor.
I worked around this by implementing a org.glassfish.jersey.server.spi.ContainerLifecycleListener
that gets a handle to the ServiceLocator and shiro's SecurityManager (through the ServletContext
which is registered with the ServiceLocator
). It then manually injects the data into the realm created by shiro.
If you're interested, I can post the code as a gist.