Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing JWT Authentication with Apache Shiro in Spring Boot

Tech 1

Core Components

Apache Shiro requires three main configurations for JWT integration:

  1. Custom Realm: Handles authentication and authorization logic by validaitng JWT tokens and retrieving user roles
  2. Security Manager: Manages security operations and connects the realm to Shiro's filter system
  3. Filter Factory: Configures URL patterns and applies JWT validation filters

JWT Filter Operations

  • preHandle: Processes CORS preflight requests
  • isAccessAllowed: Validates JWT token presence
  • executeLogin: Extracts token from Authorization header
  • getSubject: Passes authentication data to the realm

Realm Implemantation

  • supports: Specifies this realm only handles JWT tokens
  • doGetAuthenticationInfo:
    • Extracts username from JWT
    • Validates token expiration
    • Returns authentication info
  • doGetAuthorizationInfo: Assigns permissions based on user roles

Configuraton Class

@Configuration
public class SecurityConfig {
    
    @Bean
    public SecurityManager securityManager(AuthRealm realm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(realm);
        
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        SessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
        evaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(evaluator);
        manager.setSubjectDAO(subjectDAO);
        
        return manager;
    }
    
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager manager) {
        ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
        factory.setSecurityManager(manager);
        
        Map<String, Filter> filters = new HashMap<>();
        filters.put("jwt", new JwtValidationFilter());
        factory.setFilters(filters);
        
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/api/auth/**", "anon");
        filterMap.put("/static/**", "anon");
        filterMap.put("/**", "jwt");
        factory.setFilterChainDefinitionMap(filterMap);
        
        return factory;
    }
}

Custom Realm Implementation

@Component
public class AuthRealm extends AuthorizingRealm {
    
    @Autowired
    private JwtHelper jwtHelper;
    
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtAuthToken;
    }
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        String jwt = ((JwtAuthToken) token).getCredentials();
        String username = jwtHelper.extractUsername(jwt);
        
        if (jwtHelper.isTokenExpired(jwt)) {
            throw new AuthenticationException("Expired token");
        }
        
        return new SimpleAuthenticationInfo(username, jwt, getName());
    }
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        
        // Add role/permission logic here
        info.addRole("USER");
        
        return info;
    }
}

JWT Validation Filter

public class JwtValidationFilter extends BasicHttpAuthenticationFilter {
    
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        try {
            return executeLogin(request, response);
        } catch (Exception e) {
            return false;
        }
    }
    
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String token = httpRequest.getHeader("Authorization");
        
        if (StringUtils.isBlank(token)) {
            return false;
        }
        
        getSubject(request, response).login(new JwtAuthToken(token));
        return true;
    }
}

JWT Utility Class

@Component
public class JwtHelper {
    
    private final String secret = "secure-secret-key-123456";
    private final long validity = 10080; // 7 days in minutes
    
    public String generateToken(String username) {
        return Jwts.builder()
            .setSubject(username)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + validity * 60 * 1000))
            .signWith(SignatureAlgorithm.HS512, secret)
            .compact();
    }
    
    public String extractUsername(String token) {
        return Jwts.parser()
            .setSigningKey(secret)
            .parseClaimsJws(token)
            .getBody()
            .getSubject();
    }
    
    public boolean isTokenExpired(String token) {
        return Jwts.parser()
            .setSigningKey(secret)
            .parseClaimsJws(token)
            .getBody()
            .getExpiration()
            .before(new Date());
    }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.