title: Spring Boot and Apache Shiro with JWT - Am I using it correctly?
body: <p>I have Spring Boot application and I attempted to integrate Apache shiro with it. As a first iteration, I am authenticating and authorizing the JWT way, no session whatsoever.</p>
  
  <p>The way I have architected it, every REST request has to contain a JWT header that needs to be validated. I am doing it in a shiro filter. Post validation, filter sets a context, that any REST controller method will be able to fetch and act upon it.</p>
  
  <p>I want the opinion of community to make sure my configuration is correct. 
  Moreover, there are certain problems (at least IMO), I am facing with it. So if someone can throw some light on a correct way of handing it, would be greatly appreciated.</p>
  
  <p>Following are some code snippets demonstrating my configuration and realm design.</p>
  
  <p><strong>Snippet 1 : ShiroConfiguration</strong></p>
  
  <pre><code>private AuthenticationService authenticationService;
  /**
   * FilterRegistrationBean
   * @return
   */
  @Bean
  public FilterRegistrationBean filterRegistrationBean() {
      FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
      filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
      filterRegistration.setEnabled(true);
      filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
      filterRegistration.setOrder(1);
      return filterRegistration;
  }
  @Bean(name = "securityManager")
  public DefaultWebSecurityManager securityManager() {
      DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
      dwsm.setRealm(authenticationService());
      final DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
      // disable session cookie
      sessionManager.setSessionIdCookieEnabled(false);
      dwsm.setSessionManager(sessionManager);
      return dwsm;
  }
  
  /**
   * @see org.apache.shiro.spring.web.ShiroFilterFactoryBean
   * @return
   */
  @Bean(name="shiroFilter")
  public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") SecurityManager securityManager, JWTTimeoutProperties jwtTimeoutProperties, TokenUtil tokenUtil) {
      ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
      bean.setSecurityManager(securityManager);
  
      //TODO: Create a controller to replicate unauthenticated request handler
      bean.setUnauthorizedUrl("/unauthor");
  
      Map&lt;String, Filter&gt; filters = new HashMap&lt;&gt;();
      filters.put("perms", new AuthenticationTokenFilter(jwtTimeoutProperties, tokenUtil));
      filters.put("anon", new AnonymousFilter());
      bean.setFilters(filters);
  
      LinkedHashMap&lt;String, String&gt; chains = new LinkedHashMap&lt;&gt;();
      chains.put("/", "anon");
      chains.put("/favicon.ico", "anon");
      chains.put("/index.html", "anon");
      chains.put("/**/swagger-resources", "anon");
      chains.put("/api/**", "perms");
  
      bean.setFilterChainDefinitionMap(chains);
      return bean;
  }
  @Bean
  @DependsOn(value="lifecycleBeanPostProcessor")
  public AuthenticationService authenticationService() {
      if (authenticationService==null){
          authenticationService = new AuthenticationService();
      }
  
      return  authenticationService;
  }
  
  
  @Bean
  @DependsOn(value="lifecycleBeanPostProcessor")
  public Authorizer authorizer() {
      return authenticationService();
  }
  
  
  @Bean
  @DependsOn("lifecycleBeanPostProcessor")
  public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
      DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
      proxyCreator.setProxyTargetClass(true);
      return proxyCreator;
  }
  </code></pre>
  
  <p><strong>Snippet 2 : AuthenticationFilter</strong></p>
  
  <pre><code>public class AuthenticationTokenFilter extends PermissionsAuthorizationFilter {
  @Override
  public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
      HttpServletRequest httpRequest = (HttpServletRequest) request;
      String authorizationHeader = httpRequest.getHeader(TOKEN_HEADER);
      String authToken;
  
      String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
      httpRequest.setAttribute(alreadyFilteredAttributeName, true);
  
      AuthenticationService.ensureUserIsLoggedOut(); // To not end up getting following error.
  
      if (authorizationHeader != null &amp;&amp; !authorizationHeader.isEmpty()) {
  
          if (authorizationHeader.startsWith(BEARER_TOKEN_START_WITH)) {
              authToken = authorizationHeader.substring(BEARER_TOKEN_START_INDEX);
          } else if (authorizationHeader.startsWith(BASIC_TOKEN_START_WITH)) {
              String caseId = UUID.randomUUID().toString();
              log.warn("{} Basic authentication is not supported but a Basic authorization header was passed in", caseId);
              return false;
          } else {
              // if its neither bearer nor basic, default it to bearer.
              authToken = authorizationHeader;
          }
          try {
              if(tokenUtil.validateTokenAgainstSignature(authToken, jwtTimeoutProperties.getSecret())) {
                  Map&lt;String, Object&gt; outerClaimsFromToken = tokenUtil.getOuterClaimsFromToken(authToken, jwtTimeoutProperties.getSecret());
  
                  JWTAuthenticationToken jwtAuthenticationToken = new JWTAuthenticationToken(outerClaimsFromToken.get(TokenUtil.CLAIM_KEY_USERID),
                                                  (String) outerClaimsFromToken.get(TokenUtil.CLAIM_KEY_INNER_TOKEN));
                  SecurityUtils.getSubject().login(jwtAuthenticationToken);
  
          } catch (JwtException | AuthenticationException ex) {
              log.info("JWT validation failed.", ex);
          }
      }
      return false;
  }
  </code></pre>
  
  <p><strong>Snippet 3 : TokenRestController</strong></p>
  
  <pre><code>public Response getToken() {
  
      AuthenticationService.ensureUserIsLoggedOut(); // To not end up getting following error.
                                                          // org.apache.shiro.session.UnknownSessionException: There is no session with id
  
          // TODO: In case of logging in with the organization, create own token class implementing HostAuthenticationToken class.
          IAMLoginToken loginToken = new IAMLoginToken(authenticationRequestDTO.getUsername(), authenticationRequestDTO.getPassword());
          Subject subject = SecurityUtils.getSubject();
          try {
              subject.login(loginToken);
          } catch (AuthenticationException e) {
              log.debug("Unable to login", e);
              return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
          }
  
          AuthenticatingUser user = (AuthenticatingUser) subject.getPrincipal();
  
              String authToken = authenticationService.generateToken(user);
              return ResponseEntity.status(HttpStatus.OK).body(new AuthenticationResponseDTO(authToken));
      });
  </code></pre>
  
  <p><strong>Snippet 4 : AuthorizingRealm</strong></p>
  
  <pre><code>@Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
      if (token instanceof IAMLoginToken) {
          IAMLoginToken usernamePasswordToken = (IAMLoginToken) token;
  
          UserBO user = identityManagerRepository.getUserByUsername(usernamePasswordToken.getUsername(), true);
  
          if (user != null &amp;&amp; user.getSecret() != null &amp;&amp; !user.getSecret().isEmpty()) {
              if(passwordEncoder.matches(String.valueOf(usernamePasswordToken.getPassword()), user.getPassword())) {
                  if (!isActive(user)) {
                      throw new AuthenticationException("User account inactive.");
                  }
                  return new SimpleAuthenticationInfo(toAuthenticatingUser(user).withSecret(user.getSecret()), usernamePasswordToken.getPassword(), getName());
              }
          }
      } else if (token instanceof JWTAuthenticationToken) {
          JWTAuthenticationToken jwtToken = (JWTAuthenticationToken) token;
          String userId = (String) jwtToken.getUserId();
          String secret = cache.getUserSecretById(userId, false);
  
          if (secret != null &amp;&amp; !secret.isEmpty()) {
              Map&lt;String, Object&gt; tokenClaims = tokenUtil.getClaims(jwtToken.getToken(), secret);
              String orgId = (String) tokenClaims.get(TokenUtil.CLAIM_KEY_ORG);
              String email = (String) tokenClaims.get(TokenUtil.CLAIM_KEY_EMAIL);
              String firstName = (String) tokenClaims.get(TokenUtil.CLAIM_KEY_FIRSTNAME);
              String lastName = (String) tokenClaims.get(TokenUtil.CLAIM_KEY_LASTNAME);
              Set&lt;String&gt; permissions = (Set&lt;String&gt;) tokenClaims.get(TokenUtil.CLAIM_KEY_PERMISSIONS);
  
              return new SimpleAccount(new AuthenticatingUser(userId, orgId, email, firstName, lastName, permissions), jwtToken.getToken(), getName());
          }
      }
  
      throw new AuthenticationException("Invalid username/password combination!");
  }
  
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  
      SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
      authorizationInfo.setStringPermissions(((AuthenticatingUser)principals.getPrimaryPrincipal()).getPermissions());
      return authorizationInfo;
  }
  </code></pre>
  
  <p><strong>Problems and issues</strong></p>
  
  <ul>
  <li><p>Same error as mentioned in here. 
  <a href="https://stackoverflow.com/questions/14516851/shiro-complaining-there-is-no-session-with-id-xxx-with-defaultsecuritymanager">Shiro complaining &quot;There is no session with id xxx&quot; with DefaultSecurityManager</a> 
  I basically want Shiro to stop using and/or validating sessions. Is there a way to achieve that ? I solved it by implementing the same fix as mentioned in the answer, thats what <code>ensureUserIsLoggedOut()</code> does.</p></li>
  <li><p>As you can see in the configuration's ShiroFilterFactoryBean definition, I am setting some filter chain definitions. And there you can see I set every api call that starts with <code>/api</code> will have authentication filter in front. But the thing is I want to add some exceptions to it. Such as, <code>/api/v0/login</code> is one of them. Is there a way of achieving this ?</p></li>
  <li><p>Overall, I am not sure if the configuration I have come up with is appropriate, as I found very limited documentation and similar open source project samples.</p></li>
  </ul>
  
  <p>Any feedback is welcome.</p>