Information Security 13 min read

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.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Integrating Apache Shiro with Spring Boot for Permission Management

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.

JavaSpring BootsecurityThymeleafPermission Managementshiro
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login 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.