Integrating Apache Shiro with Spring Boot for Permission Management
This article demonstrates how to integrate the Apache Shiro security framework into a Spring Boot application, covering Maven dependencies, Shiro configuration, custom Realm implementation, login authentication, controller-level access control, and Thymeleaf front‑end button visibility based on roles and permissions.
Apache Shiro is a powerful Java security framework that provides authentication, authorization, password, and session management. This tutorial explains how to incorporate Shiro into a Spring Boot project to control user page and button access.
1. Adding Maven Dependencies
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring-boot-web-starter -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>2. Shiro Configuration (ShiroConfig)
package com.example.config;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// Interceptor chain definition
Map
filterChainDefinitionMap = new LinkedHashMap<>();
// URLs that can be accessed anonymously
filterChainDefinitionMap.put("/static/**", "anon");
// Logout URL handled by Shiro
filterChainDefinitionMap.put("/logout", "logout");
// All other URLs require authentication
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setSuccessUrl("/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean(name="defaultWebSecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") MyShiroRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
@Bean(name="userRealm")
public MyShiroRealm getUserRealm(){
return new MyShiroRealm();
}
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}3. Custom Realm (MyShiroRealm)
package com.example.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.example.pojo.Permission;
import com.example.pojo.Role;
import com.example.pojo.User;
import com.example.service.RoleService;
import com.example.service.UserService;
@Component("authorizer")
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = (User)principals.getPrimaryPrincipal();
System.out.println("User:"+user+" roles count:"+user.getRoles().size());
for(Role role : user.getRoles()){
authorizationInfo.addRole(role.getId());
role = roleService.getRoleById(role.getId());
System.out.println("Role:"+role);
for(Permission p : role.getPermissions()){
System.out.println("Permission:"+p);
authorizationInfo.addStringPermission(p.getCode());
}
}
System.out.println("权限配置-->authorizationInfo"+authorizationInfo.toString());
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
String username = (String)token.getPrincipal();
System.out.println(token.getCredentials());
User user = userService.getUserById(username);
System.out.println("---->>userInfo="+user);
if(user == null){
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user,
"123456", // password placeholder
getName()
);
return authenticationInfo;
}
}4. Login Authentication
A simple HTML login page is provided, and the LoginController processes GET and POST requests, creating a UsernamePasswordToken and invoking subject.login() . Errors such as unknown account or incorrect credentials are handled by returning to the login view with an appropriate message.
package com.example.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class LoginController {
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(){
return "login";
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(Model model, String id, String pwd){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(id, "123456");
try {
subject.login(token);
return "home";
} catch (UnknownAccountException e) {
model.addAttribute("msg", "用户名不存在");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误");
return "login";
}
}
@RequestMapping(value = "/index")
public String index(){
return "home";
}
}5. Controller‑Level Access Control
Methods are protected with Shiro annotations such as @RequiresRoles("002") and @RequiresPermissions("002") . When a user lacking the required role or permission accesses the endpoint, Shiro throws an authorization exception, which can be mapped to an error page.
@RequestMapping(value = "/edit", method = RequestMethod.GET)
@RequiresRoles("002")
public String editGet(Model model, @RequestParam(value="id") String id) {
model.addAttribute("id", id);
return "/user/edit";
}
@RequestMapping(value = "/selrole", method = RequestMethod.GET)
@RequiresPermissions("002")
public String selctRole(Model model, @RequestParam("id") String id, @RequestParam("type") Integer type) {
model.addAttribute("id", id);
model.addAttribute("type", type);
return "/user/selrole";
}6. Front‑End Button Visibility with Thymeleaf
By adding the xmlns:shiro namespace to the HTML template, buttons can be shown or hidden based on the current user's roles using shiro:hasAnyRoles="002,003" . This prevents unauthorized users from seeing or clicking restricted UI elements.
<html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
...
<a shiro:hasAnyRoles="002,003" class="layui-btn layui-btn-normal" onclick="addUser('')">添加用户</a>
<a shiro:hasAnyRoles="002,003" class="layui-btn layui-btn-danger" onclick="getDatas();">批量删除</a>
</html>7. Summary
The guide provides a functional example of integrating Shiro for authentication and authorization in a Spring Boot application, covering backend configuration, custom Realm logic, controller protection, and front‑end role‑based UI control. It also mentions advanced topics such as session management, Redis integration, and SSO for future enhancements.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.