Secure Your Spring Boot App with Apache Shiro: Auth, Authz, and Redis

This article walks through integrating Apache Shiro into a Spring Boot project to handle authentication, authorization, session management, and caching, including custom realms, Redis-backed sessions, and cache managers, while providing detailed code examples and configuration guidance.

Programmer DD
Programmer DD
Programmer DD
Secure Your Spring Boot App with Apache Shiro: Auth, Authz, and Redis

Introduction

Throughout an application's lifecycle, data security—especially user legitimacy and data visibility—is crucial. In micro‑service architectures, sharing sessions and caching authentication/authorization data under high concurrency becomes a challenge, which Apache Shiro addresses with a simple yet powerful API.

What Is Shiro?

Apache Shiro™ is a robust, easy‑to‑use Java security framework that supports authentication, authorization, cryptography, and session management. Its API is straightforward, allowing any application—from tiny mobile apps to large enterprise systems—to become secure without requiring Spring.

Shiro vs. Spring Security

Shiro is lightweight and can operate independently of Spring, offering coarser granularity, while Spring Security is more complex, tightly coupled to Spring, and provides finer‑grained controls.

Core Components

Subject

The Subject represents the current user (or any interacting entity such as a bot). All interactions are delegated to the SecurityManager.

SecurityManager

The SecurityManager is the heart of Shiro, managing all Subjects and coordinating with other components, similar to Spring MVC’s DispatcherServlet.

Realm

Realms are data sources for authentication and authorization information. Shiro queries a Realm to verify credentials and retrieve roles/permissions.

SessionManager & SessionDAO

These manage the lifecycle of sessions, allowing custom storage (e.g., Redis) for distributed environments.

CacheManager

Handles caching of security data such as permissions to improve performance.

Cryptography

Provides utilities for password hashing and encryption.

Setup Overview

Most developers use Spring Boot, and Shiro offers a starter for easy integration. The typical dependencies include Spring Boot, JPA, MySQL, Redis, Lombok, Guava, and Shiro.

After adding the starter, configure application.yml (see image) to enable Shiro filters and session settings.

Authentication

User Entity

@Data
@Entity
public class User implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(unique = true)
    private String username;

    private String password;
    private String salt;
}

User Repository

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findUserByUsername(String username);
}

Login Controller

@GetMapping("/login")
public void login(String username, String password) {
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    token.setRememberMe(true);
    Subject currentUser = SecurityUtils.getSubject();
    currentUser.login(token);
}

Custom Realm (Authentication)

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    String username = token.getUsername();
    User user = userRepository.findUserByUsername(username);
    SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
    info.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt()));
    return info;
}

Key questions: how Shiro is applied, how the controller reaches the custom realm, and the purpose of overriding doGetAuthenticationInfo (to provide authentication data from the application’s data source).

Authorization

Authorization verifies what actions a user can perform. Shiro provides annotations such as @RequiresPermissions, @RequiresRoles, @RequiresAuthentication, etc.

Role Entity & Repository

@Data
@Entity
public class Role {
    @Id
    @GeneratedValue(strategy = AUTO)
    private Long id;

    @Column(unique = true)
    private String roleCode;
    private String roleName;
}
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
    @Query("select roleId from UserRoleRel ur where ur.userId = ?1")
    List<Long> findUserRole(Long userId);

    List<Role> findByIdIn(List<Long> ids);
}

Permission Entity & Repository

@Data
@Entity
public class Permission {
    @Id
    @GeneratedValue(strategy = AUTO)
    private Long id;

    @Column(unique = true)
    private String permCode;
    private String permName;
}
@Repository
public interface PermissionRepository extends JpaRepository<Permission, Long> {
    @Query("select permId from RolePermRel pr where pr.roleId in ?1")
    List<Long> findRolePerm(List<Long> roleIds);

    List<Permission> findByIdIn(List<Long> ids);
}

Custom Realm (Authorization)

The realm’s doGetAuthorizationInfo method assembles the user’s roles and permissions from the repositories, enabling Shiro’s annotation‑driven checks.

Session Management

Shiro’s session concepts mirror traditional web sessions but can be customized. Important configuration keys (e.g., shiro.sessionManager.cookie.name, shiro.loginUrl) are listed in the original table.

Redis Integration

To store sessions in Redis for distributed systems, define a RedisConfig bean and a custom RedisSessionDao that reads/writes session data via RedisTemplate.

@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public RedisTemplate<String, Object> stringObjectRedisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}

Session beans are then wired to use the custom DAO:

@Bean(name = "customerWebSessionManager")
public CustomerWebSessionManager customerWebSessionManager() {
    CustomerWebSessionManager manager = new CustomerWebSessionManager();
    manager.setSessionDAO(redisSessionDao());
    return manager;
}

@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(CustomRealm customRealm) {
    DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    manager.setRealm(customRealm);
    manager.setSessionManager(customerWebSessionManager());
    manager.setCacheManager(redisCacheManagers());
    return manager;
}

Cache Management

To avoid frequent database hits for permission data, Shiro’s cache can be backed by Redis. A RedisCache implementation stores entries with a shiro-cache: prefix, and a RedisCacheManager returns this cache instance.

@Component
public class RedisCache<K, V> implements Cache<K, V> {
    public static final String SHIRO_PREFIX = "shiro-cache:";
    @Resource
    private RedisTemplate<String, Object> stringObjectRedisTemplate;

    private String getKey(K key) {
        return (key instanceof String) ? SHIRO_PREFIX + key : key.toString();
    }

    @Override
    public V get(K k) {
        return (V) stringObjectRedisTemplate.opsForValue().get(getKey(k));
    }

    @Override
    public V put(K k, V v) {
        stringObjectRedisTemplate.opsForValue().set(getKey(k), v);
        stringObjectRedisTemplate.expire(getKey(k), 100, java.util.concurrent.TimeUnit.SECONDS);
        return v;
    }

    @Override
    public V remove(K k) {
        V v = get(k);
        stringObjectRedisTemplate.delete(getKey(k));
        return v;
    }

    // size, keys, values, clear are left as no‑ops for simplicity
    @Override public void clear() {}
    @Override public int size() { return 0; }
    @Override public java.util.Set<K> keys() { return null; }
    @Override public java.util.Collection<V> values() { return null; }
}

public class RedisCacheManager implements CacheManager {
    @Resource
    private RedisCache redisCache;
    @Override
    public <K, V> Cache<K, V> getCache(String name) {
        return redisCache;
    }
}

Conclusion

The guide outlines the full process of integrating Shiro with Spring Boot, customizing authentication and authorization via realms, and enhancing scalability with Redis‑backed sessions and caches. Readers are encouraged to explore the demo code and the official Shiro documentation for deeper understanding.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

redisSpring BootAuthenticationAuthorizationApache ShiroJava Security
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.