Reputation: 125
I'm attempting to adapt this excellent stormpath post by Brian Demers - https://stormpath.com/blog/protecting-jax-rs-resources-rbac-apache-shiro - to my own purposes and so far it works pretty well - except that now I want to add stormpath for user/role management rather then having the users in a shiro-ini file.
I'm using Apache Shiro shiro-jaxrs 1.4.0-RC to secure a REST endpoint using jax-rs. It works fine. I'm able to selectively secure the endpoints using a @RequiresPermissions tag like so:
@Path("/scan")
@Produces("application/json")
public class ScanService {
final static Logger logger = Logger.getLogger(ScanService.class);
@GET
@Path("/gettest")
@RequiresPermissions("troopers:read")
public List<Barcode> gettest() throws Exception {
ArrayList<Barcode> listofstrings = new ArrayList<Barcode>();
Barcode b = new Barcode();
b.setBarcode("this is a big barcode");
listofstrings.add(b );
return listofstrings;
}
@GET
@Produces( MediaType.APPLICATION_JSON )
@Path("/gettest2")
public List<Barcode> gettest2() throws Exception {
ArrayList<Barcode> listofstrings = new ArrayList<Barcode>();
Barcode b = new Barcode();
b.setBarcode("this is a BIGGER barcode");
listofstrings.add(b );
return listofstrings;
}
I also have an application class to add my resource and the ShiroFeature class like so:
package ca.odell.erbscan;
import ca.odell.erbscan.ws.ScanService;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.web.jaxrs.ShiroFeature;
import com.stormpath.shiro.jaxrs.StormpathShiroFeature;
@ApplicationPath("/")
public class ERBApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
// register Shiro
classes.add( ShiroFeature.class);
// register resources
classes.add(ScanService.class);
return classes;
}
}
and my web.xml to init my Application class like so:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>ERBSCAN</display-name>
<servlet>
<servlet-name>ERBRest</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>ca.odell.erbscan</param-value>
</init-param>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>ca.odell.erbscan.ERBApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ERBRest</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</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>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
</web-app>
and finally my shiro.ini
[main]
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $cacheManager
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.sessionIdCookieEnabled = false
securityManager.sessionManager.sessionIdUrlRewritingEnabled = false
[urls]
/** = noSessionCreation, authcBasic[permissive]
[users]
# format: username = password, role1, role2, ..., roleN
root = secret,admin
emperor = secret,admin
officer = secret,officer
guest = secret
[roles]
admin = *
officer = troopers:create, troopers:read, troopers:update
What I want to do next is add Stormpath for RBAC rather then having users and roles in a file. My feeling is there's a simple way to do this and that I'm overthinking it.
I thought it would be a fairly straightforward manner of adding in my shiro.ini:
stormpathClient = com.stormpath.shiro.client.ClientFactory
stormpathClient.cacheManager = $cacheManager
stormpath.application.href=http://....
But I was wrong. Could someone point me in the right direction?
Upvotes: 1
Views: 272
Reputation: 2080
thanks for reading that post!
A couple things I want to point out:
com.stormpath.shiro.jaxrs.StormpathShiroFeature
instead of ShiroFeature
[main]
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $cacheManager
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.sessionIdCookieEnabled = false
securityManager.sessionManager.sessionIdUrlRewritingEnabled = false
[urls]
/** = noSessionCreation, authcBasic[permissive]
[stormpath]
stormpath.application.href=http://....
{
… your other custom data fields …,
"apacheShiroPermissions": [
"troopers:create",
"troopers:read",
"troopers:update"
]
}
This blog post covers the custom data bit, it is a little older, but still relevant. I'll be updating the doc on this in the near future, so feedback welcome.
If this doesn't help you can also ping support, and we will get you going!
Upvotes: 1
Reputation: 125
I'm going to answer my own question here. I don't think this is the best solution, but it something I managed to get to work.
I followed this web app tutorial off of the shiro site.
https://shiro.apache.org/webapp-tutorial.html
I checked out step6 of the project and copied the [main] section of the shiro.ini as follows: Note I added the
https://api.stormpath.com/v1/applications/$STORMPATH_APPLICATION_ID
at the bottom the [main] section.
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $cacheManager
stormpathClient = com.stormpath.shiro.client.ClientFactory
stormpathClient.cacheManager = $cacheManager
# we can disable session tracking completely, and have Stormpath manage it for us.
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.sessionIdCookieEnabled = false
securityManager.sessionManager.sessionIdUrlRewritingEnabled = false
stormpathRealm = com.stormpath.shiro.realm.ApplicationRealm
stormpathRealm.client = $stormpathClient
stormpathRealm.groupRoleResolver.modeNames = name
securityManager.realm = $stormpathRealm
stormpathRealm.applicationRestUrl = https://api.stormpath.com/v1/applications/$STORMPATH_APPLICATION_ID
I then completely removed the [users] section of the shiro.ini. Since it's now wired up to Stormpath, I need to add users and groups there. My ScanService ( as above ) has a method called gettest decorated thusly:
@GET
@Path("/gettest")
@RequiresPermissions("trooper:read")
public List<Barcode> gettest() throws Exception {
.
.
.
so I need to added an account, a group and permissions in stormpath to match the permissions on the above resource. In order to do this, I need to add an account in Stormpath ( I already have the Application setup ) under my existing test application. I also added a group called officer1. The under this group I added Custom Data an array called apacheShiroPermissions - I added a string key/value pair 'trooper:read' to the apacheShiroPermissions - the JSON is below
{
"apacheShiroPermissions": [
"trooper:read"
]
}
Then I simply made sure my account - in this case jlpicard was part of the officer1 group.
Testing with curl
curl --user jlpicard:Changeme1 http://localhost:8080/JPA1_Web_exploded/rest/scan/gettest
Confirms jlpicard has access on the permission level. Adding and removing the strings entry's to the apacheShiroPermission array i.e. allows that fine grained access.
Also removing jlpicard from the officer1 or adding another account to it works as expected.
There is undoubtedly a better way to do this but this has what was worked for me so far.
Upvotes: 0