title: Multiple realms to handle authentication for different sets of urls in Spring MVC and shiro
body: <p>I am trying to find a best practice solution for my situation. I have two different sets of urls in my webapp that need to be secured/authenticated against two different sets of user bases.</p>
  
  <p>For example, the /foo/* urls are to be accessible only to a certain set of users based on username/pwd tokens. And another set /bar/* urls are to be accessible only to a set of users (stored in a different database table from the first set) via username/pwd token mechanism.</p>
  
  <p>I want to understand how to structure the shiro filter(s) in my web.xml and the corresponding shiro filter bean definition in my applicationContext.xml. Should I be using two different shiro filters (one for each url pattern), mapping to two different bean in the context file, where each bean has its own security manager that is configured with its own realm (each realm responsible for authenticating against the corresponding user base table).</p>
  
  <p>Here I am following this two links:</p>
  
  <p><a href="https://stackoverflow.com/questions/9268523/multi-tenancy-in-shiro/24545588#24545588">multi-tenancy-in-shiro</a></p>
  
  <p><a href="http://shiro-user.582556.n2.nabble.com/Multiple-security-managers-and-realms-to-handle-authentication-for-different-sets-of-urls-td7445068.html#a7579763" rel="nofollow noreferrer">Multiple-security-managers...</a></p>
  
  <p>but I am getting errors in my application. Here I am pasting some sample code.</p>
  
  <p>ApplicationContext-shiro.xml
      </p>
  
  <pre><code>    &lt;!-- Single realm app (realm configured next, below). If you have multiple 
          realms, use the 'realms' property instead. --&gt;
      &lt;property name="realms"&gt;
          &lt;util:list&gt;
              &lt;ref bean="adminAuthRealm" /&gt;
              &lt;ref bean="vendorAuthRealm" /&gt;              
          &lt;/util:list&gt;
      &lt;/property&gt;
  
      &lt;!-- &lt;property name="authenticator"&gt;
          &lt;bean class="com.yatra.mp.security.MultiLoginAuthenticator"/&gt;
      &lt;/property&gt; --&gt;
  
      &lt;property name="cacheManager" ref="cacheManager" /&gt;
      &lt;!-- Uncomment this next property if you want heterogenous session access 
          or clusterable/distributable sessions. The default value is 'http' which 
          uses the Servlet container's HttpSession as the underlying Session implementation. --&gt;
  &lt;/bean&gt;`&lt;bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"&gt;
      &lt;property name="securityManager" ref="securityManager" /&gt;
      &lt;property name="unauthorizedUrl" value="/permissionDenied.do" /&gt;
  
      &lt;!-- The 'filters' property is usually not necessary unless performing 
          an override, which we want to do here (make authc point to a PassthruAuthenticationFilter 
          instead of the default FormAuthenticationFilter: --&gt;
  
      &lt;property name="filters"&gt;
          &lt;util:map&gt;
              &lt;entry key="adminAuthc" value-ref="adminAuthc" /&gt;
              &lt;entry key="vendorAuthc" value-ref="vendorAuthc" /&gt;
              &lt;entry key="adminUser" value-ref="adminUser" /&gt;
          &lt;/util:map&gt;
      &lt;/property&gt;
      &lt;property name="filterChainDefinitions"&gt;
          &lt;value&gt;
              /admin/welcome.do = anon
              /vendor/welcome.do = anon
              /vendor/signup.do = anon
              /admin/signup.do = anon
              /assets/** = anon
              /admin/** = adminAuthc
              /vendor/** = vendorAuthc
          &lt;/value&gt;
      &lt;/property&gt;
  &lt;/bean&gt;
  &lt;bean id="adminAuthc"
      class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter"&gt;
      &lt;property name="loginUrl" value="/admin/login.do" /&gt;
      &lt;property name="successUrl" value="/admin/home.do" /&gt;
  &lt;/bean&gt;
  &lt;bean id="vendorAuthc"
      class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter"&gt;
      &lt;property name="loginUrl" value="/vendor/login.do" /&gt;
      &lt;property name="successUrl" value="/vendor/home.do" /&gt;
  &lt;/bean&gt;
  &lt;bean id="adminUser" class="org.apache.shiro.web.filter.authc.UserFilter"&gt;
      &lt;property name="loginUrl" value="/admin/login.do" /&gt;
  &lt;/bean&gt;
  
  &lt;bean id="vendorUser" class="org.apache.shiro.web.filter.authc.UserFilter"&gt;
      &lt;property name="loginUrl" value="/vendor/login.do" /&gt;
  &lt;/bean&gt;`
  </code></pre>
  
  <p>MultiLoginAuthenticator.java</p>
  
  <pre><code>public class MultiLoginAuthenticator extends ModularRealmAuthenticator {
  
  @Override
  protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
      assertRealmsConfigured();   &lt;------- Here I am getting null getRealms(). So I am getting exceptions
      MultiLoginAuthenticationToken mlat = null;
      Realm loginRealm = null;
  
      if (!(authenticationToken instanceof MultiLoginAuthenticationToken)) {
          throw new AuthenticationException("Unrecognized token , not a typeof MultiLoginAuthenticationToken ");
      } else {
          mlat = (MultiLoginAuthenticationToken) authenticationToken;
          loginRealm = lookupRealm(mlat.getRealmName());
      }
  
      return doSingleRealmAuthentication(loginRealm, mlat);
  
  }
  
  protected Realm lookupRealm(String realmName) throws AuthenticationException {
      Collection&lt;Realm&gt; realms = getRealms();
      for (Realm realm : realms) {
          if (realm.getName().equalsIgnoreCase(realmName)) {
              return realm;
          }
      }
      throw new AuthenticationException("No realm configured for Client " + realmName);
  }
  </code></pre>
  
  <p>}</p>
  
  <p>while i am configuring MultiLoginAuthenticator, i am getting null in getReamls() method. So it is throwing IllegalStateException.</p>
  
  <p>If I remove this Authenticator configuration from applicationContext.xml file, then it is able to login correctly but it is calling different Realm for "<strong>doGetAuthorizationInfo</strong>" method. So It is not assigning any role and permission to that current Subject.</p>
  
  <p>This is my Realm file (both Realms are same. Difference is both are calling different datasource.)</p>
  
  <pre><code>@Override
  public boolean supports(AuthenticationToken token) {
      if (token instanceof MultiLoginAuthenticationToken) {
          return ((MultiLoginAuthenticationToken)token).getRealmName().equalsIgnoreCase("VendorAuthRealm");
      }
      return false;
  }
  
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(
          PrincipalCollection principals) {
      logger.debug("authorization info.....");
      logger.debug("realm size is : {}", principals.fromRealm(getName()).size());
      logger.debug("realm name is : {}", principals.fromRealm(getName()));
  
      int userId = (Integer) principals.fromRealm(getName()).iterator().next();
  
      VendorUser vendorUser = vendorUserService.getVendorUser(userId);
      if(vendorUser != null) {
          logger.debug("vendor user first name is : {}", vendorUser.getFirstName());
          SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
          if(vendorUser.getVendorProducts() != null){
              logger.debug("vendor users products and rate types...");
              info.addRoles(VendorYatraUtil.getProductSet(vendorUser.getVendorProducts()));
              info.addStringPermissions(VendorYatraUtil.getCarRateTypeSet(vendorUser.getVendorCarRateTypes()));
          }
          return info;
      }
      return null;
  }
  
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(
          AuthenticationToken authcToken) throws AuthenticationException {
      MultiLoginAuthenticationToken token = (MultiLoginAuthenticationToken) authcToken;
      logger.debug("username is : {}", token.getUsername());
  
      if(vendorUserService.findVendorUser(token.getUsername()) != null){
          VendorUser vendorUser = vendorUserService.findVendorUser(token.getUsername()); 
          if (vendorUser != null){
              return new SimpleAuthenticationInfo(vendorUser.getId(),
                      vendorUser.getPassword(), getName());
          }
      }
  
      return null;
  }
  </code></pre>
  
  <p>Thanks,
  Ankit</p>