Wednesday, November 14, 2012

Example Shiro setup w/o database backing in grails

When we decided to implement Shiro for our roles based authentication and authorization in our grails app we were initially excited but it turned into a real beast to get into our system. The biggest problem was getting it to work on a system that uses api calls and doesn't directly access the database to auth users.  There was no good example or documentation for this approach out there so it was difficult.  Here are the basics to how we solved it.

First we added some beans to the applicationContext.xml

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
   <property name="securityManager" ref="securityManager"/>
   <!-- override these for application-specific URLs if you like:-->
   <property name="loginUrl" value="/"/>
   <property name="successUrl" value="/search/newSearch"/>
   <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
   <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean  -->
   <!-- defined will be automatically acquired and available via its beanName in chain        -->
   <!-- definitions, but you can perform instance overrides or name aliases here if you like: -->
        <property name="filters">
                <entry key="authc">
                    <bean class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter"/>
   <property name="filterChainDefinitions">
           # some example chain definitions:
           /search/** = authc
           /session/** = anon
           /internal/** = anon
           /** = anon
           # more URL-to-FilterChain definitions here

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
   <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
   <property name="realm" ref="exampleRealm"/>
   <property name="cacheManager" ref="shiroCacheManager"/>
   <!-- By default the servlet container sessions will be used.  Uncomment this line
        to use shiro's native sessions (see the JavaDoc for more): -->
   <!-- <property name="sessionMode" value="native"/> -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<bean id="allCredentialsMatcher" class="org.apache.shiro.authc.credential.AllowAllCredentialsMatcher" />

<!-- Define the Shiro Realm implementation you want to use to connect to your back-end -->
<!-- security datasource: -->
<bean id="exampleRealm" class="com.ourcompany.otherstuff.ExampleRealm" >
<property name="accountDomain" >
<ref bean="accountDomain" />
<property name="credentialsMatcher">
<ref local="allCredentialsMatcher" />
<property name="authorizationCacheName">

<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">

<bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="ehCacheManager"/>

<bean id="permissionLoader" class="com.ourcompany.otherstuff.permission.PermissionLoader" init-method="load">
<constructor-arg ref="shiroCacheManager" />

In the web.xml we added the following:

<!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->

<!-- Shiro mapping should go first so it can function in the next ones -->

Then we created some classes

The PermissionLoader class basically extended from ApplicationContextAware a springframework class.  It set the cachemanager in the constructor and in the load() method it got the permissions cache and filled it with our permissions listed in a config file.

The heavy hitter here was the ExampleRealm class.  That class is defined as such

class ExampleRealm extends AuthorizingRealm {

public ExampleRealm() {
setName( "ExampleRealm" )
boolean supports( AuthenticationToken token ) {
return true;
protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authToken ) throws AuthenticationException {
        // this method returns an authorized user as a valid AuthenticationInfo object else it throws an exception

public boolean isPermitted(PrincipalCollection principals, Permission permission) {
             // returns whether the user specified in principals has the passed in permission

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
              // returns a user object as an AuthorizationInfo with their permissions filled in

After that and that was a bunch.  We created a UserService and we defined the auth method something like this:
    def authenticate( email, password ) throws AuthenticationException {
        def authToken = new UsernamePasswordToken(username, password )
        // Perform the actual login. An AuthenticationException
        // will be thrown from ExampleRealm  if the username is unrecognised or the
        // password is incorrect.

Finally the jsps

After all that we were able to use things like the cool jsp tags like
<%@ taglib prefix="shiro" uri="" %>

<shiro:hasPermission name="Identity:Update:Module">

Good luck out there and stay strong don't let Shiro beat you!

No comments:

Post a Comment