Integrating Apache Shiro with Spring Boot: Core Components, Configuration, and Authentication Flow
This article explains how to integrate the Apache Shiro security framework into a Spring Boot application, covering core components, Maven configuration, bean definitions, custom realms, filter chains, and the complete login authentication process with code examples.
In Java projects, security frameworks such as Apache Shiro and Spring Security are commonly used; Shiro is a lightweight, intuitive, and robust open‑source security manager.
Shiro’s three core components are Subject , SecurityManager , and Realm (see the diagrams below).
Spring Boot Integration Steps
1. Manage Shiro version
<properties>
<shiro.version>1.6.0</shiro.version>
<java.version>1.8</java.version>
<jmeter.version>5.4.1</jmeter.version>
</properties>2. Add Maven dependency
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>${shiro.version}</version>
</dependency>3. Create ShiroConfig class
Define three beans: ShiroFilterFactoryBean , DefaultWebSecurityManager , and a custom Realm .
Example of the securityManager bean:
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager(SessionManager sessionManager, MemoryConstrainedCacheManager memoryConstrainedCacheManager) {
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setSessionManager(sessionManager);
dwsm.setCacheManager(memoryConstrainedCacheManager);
dwsm.setAuthenticator(modularRealmAuthenticator());
return dwsm;
}Custom ModularRealmAuthenticator that stops after the first successful realm:
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator() {
UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
return modularRealmAuthenticator;
}Configuration of ShiroFilterFactoryBean with login, unauthorized, and success URLs, and custom filters ( apikey , csrf , user ) as well as a filter‑chain map:
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager sessionManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setSecurityManager(sessionManager);
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setSuccessUrl("/");
shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
shiroFilterFactoryBean.getFilters().put("csrf", new CsrfFilter());
shiroFilterFactoryBean.getFilters().put("user", new UserAuthcFilter());
Map
filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap);
ShiroUtils.ignoreCsrfFilter(filterChainDefinitionMap);
filterChainDefinitionMap.put("/**", "apikey, csrf, authc");
return shiroFilterFactoryBean;
}Utility methods for filter‑chain definitions:
public static void loadBaseFilterChain(Map
filterChainDefinitionMap) {
filterChainDefinitionMap.put("/resource/**", "anon");
filterChainDefinitionMap.put("/*.worker.js", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/signin", "anon");
}
public static void ignoreCsrfFilter(Map
filterChainDefinitionMap) {
filterChainDefinitionMap.put("/", "apikey, authc");
filterChainDefinitionMap.put("/language", "apikey, authc");
filterChainDefinitionMap.put("/test/case/file/preview/**", "apikey, authc");
}4. Custom LdapRealm
Extend AuthorizingRealm and implement doGetAuthenticationInfo and doGetAuthorizationInfo :
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String userId = token.getUsername();
String password = String.valueOf(token.getPassword());
return loginLdapMode(userId, password);
}
private AuthenticationInfo loginLdapMode(String userId, String password) {
// retrieve user, validate, then return
return new SimpleAuthenticationInfo(userId, password, getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userId = (String) principals.getPrimaryPrincipal();
return getAuthorizationInfo(userId, userService);
}
public static AuthorizationInfo getAuthorizationInfo(String userId, UserService userService) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
UserDTO userDTO = userService.getUserDTO(userId);
Set
roles = userDTO.getRoles().stream().map(Role::getId).collect(Collectors.toSet());
authorizationInfo.setRoles(roles);
return authorizationInfo;
}Application Example – Login Authentication
The login flow: the client posts credentials, the controller creates a UsernamePasswordToken , calls Subject.login(token) , the SecurityManager delegates to the Authenticator , which invokes the configured LdapRealm to verify the credentials and return an AuthenticationInfo . Upon success, the subject is marked authenticated and user data is stored in the session.
@PostMapping(value = "/signin")
public ResultHolder login(@RequestBody LoginRequest request) {
SessionUser sessionUser = SessionUtils.getUser();
if (sessionUser != null && !StringUtils.equals(sessionUser.getId(), request.getUsername())) {
return ResultHolder.error(Translator.get("please_logout_current_user"));
}
SecurityUtils.getSubject().getSession().setAttribute("authenticate", UserSource.LOCAL.name());
return userService.login(request);
}
public ResultHolder login(LoginRequest request) {
String login = (String) SecurityUtils.getSubject().getSession().getAttribute("authenticate");
String username = StringUtils.trim(request.getUsername());
String password = "";
if (!StringUtils.equals(login, UserSource.LDAP.name())) {
password = StringUtils.trim(request.getPassword());
}
UsernamePasswordToken token = new UsernamePasswordToken(username, password, login);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
if (subject.isAuthenticated()) {
return ResultHolder.success(subject.getSession().getAttribute("user"));
} else {
return ResultHolder.error(Translator.get("login_fail"));
}
} catch (ExcessiveAttemptsException e) {
throw new ExcessiveAttemptsException(Translator.get("excessive_attempts"));
}
}Diagram (omitted) illustrates the request flow from client to Shiro components.
Conclusion
Apache Shiro is a powerful, flexible open‑source security framework that handles authentication, authorization, and session management with ease; this guide demonstrates its integration into a Spring Boot project, covering core configuration, custom realms, filter chains, and a basic login workflow.
360 Tech Engineering
Official tech channel of 360, building the most professional technology aggregation platform for the brand.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.