Information Security 17 min read

Integrating Apache Shiro with Spring MVC: Detailed Example and Configuration Guide

This article provides a step‑by‑step tutorial on using Apache Shiro for authentication, authorization, session management and encryption in a Java web project, covering Maven dependencies, custom Realm implementation, Spring XML configuration, servlet filter setup, controller logic, JSP login page, demo screenshots and common pitfalls.

Java Captain
Java Captain
Java Captain
Integrating Apache Shiro with Spring MVC: Detailed Example and Configuration Guide

Shiro Framework Overview

Apache Shiro is a Java security framework that simplifies authentication, authorization, session management and cryptography for both Java SE and Java EE applications. Its core features include login verification, permission checks, role assignments, caching, multi‑thread support, testing utilities and a "remember me" option.

Project Environment

The example uses Eclipse with Maven and the main technologies are Spring, Spring MVC and Shiro.

1. Maven Dependencies

<!-- Spring integration for Shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.2.1</version>
</dependency>

2. Custom Shiro Realm

package com.luo.shiro.realm;

import java.util.HashSet;
import java.util.Set;
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.authc.UsernamePasswordToken;
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 com.luo.util.DecriptUtil;

public class MyShiroRealm extends AuthorizingRealm {
    private static final String USER_NAME = "luoguohui";
    private static final String PASSWORD = "123456";

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Set
roleNames = new HashSet
();
        Set
permissions = new HashSet
();
        roleNames.add("administrator"); // add role
        permissions.add("newPage.jhtml"); // add permission
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
        info.setStringPermissions(permissions);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        if (token.getUsername().equals(USER_NAME)) {
            return new SimpleAuthenticationInfo(USER_NAME, DecriptUtil.MD5(PASSWORD), getName());
        } else {
            throw new AuthenticationException();
        }
    }
}

3. Spring‑Shiro XML Configuration (spring-shiro.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
       default-lazy-init="true">

    <description>Shiro Configuration</description>

    <!-- SecurityManager bean -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myShiroRealm" />
        <property name="cacheManager" ref="cacheManager" />
    </bean>

    <!-- Custom Realm bean -->
    <bean id="myShiroRealm" class="com.luo.shiro.realm.MyShiroRealm">
        <property name="cacheManager" ref="cacheManager" />
    </bean>

    <!-- Shiro filter bean -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login.jhtml" />
        <property name="successUrl" value="/loginsuccess.jhtml" />
        <property name="unauthorizedUrl" value="/error.jhtml" />
        <property name="filterChainDefinitions">
            <value>
                /index.jhtml = authc
                /login.jhtml = anon
                /checkLogin.json = anon
                /loginsuccess.jhtml = anon
                /logout.json = anon
                /** = authc
            </value>
        </property>
    </bean>

    <!-- Cache manager bean -->
    <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />

    <!-- Lifecycle bean post‑processor -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    <!-- Enable Shiro AOP annotations -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>

</beans>

4. web.xml Filter Registration

<!-- Load Spring and Shiro configuration files -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:application.xml,classpath:shiro/spring-shiro.xml</param-value>
</context-param>

<!-- Shiro filter definition -->
<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>*.jhtml</url-pattern>
    <url-pattern>*.json</url-pattern>
</filter-mapping>

5. Controller Code (UserController)

package com.luo.controller;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.druid.support.json.JSONUtils;
import com.luo.errorcode.LuoErrorCode;
import com.luo.exception.BusinessException;
import com.luo.util.DecriptUtil;

@Controller
public class UserController {

    @RequestMapping("/index.jhtml")
    public ModelAndView getIndex(HttpServletRequest request) throws Exception {
        return new ModelAndView("index");
    }

    @RequestMapping("/login.jhtml")
    public ModelAndView login() throws Exception {
        return new ModelAndView("login");
    }

    @RequestMapping("/loginsuccess.jhtml")
    public ModelAndView loginsuccess() throws Exception {
        return new ModelAndView("loginsuccess");
    }

    @RequestMapping(value="/checkLogin.json", method=RequestMethod.POST)
    @ResponseBody
    public String checkLogin(String username, String password) {
        Map
result = new HashMap
();
        try {
            UsernamePasswordToken token = new UsernamePasswordToken(username, DecriptUtil.MD5(password));
            Subject currentUser = SecurityUtils.getSubject();
            if (!currentUser.isAuthenticated()) {
                token.setRememberMe(true);
                currentUser.login(token);
            }
        } catch (Exception ex) {
            throw new BusinessException(LuoErrorCode.LOGIN_VERIFY_FAILURE);
        }
        result.put("success", true);
        return JSONUtils.toJSONString(result);
    }

    @RequestMapping(value="/logout.json", method=RequestMethod.POST)
    @ResponseBody
    public String logout() {
        Map
result = new HashMap
();
        result.put("success", true);
        Subject currentUser = SecurityUtils.getSubject();
        currentUser.logout();
        return JSONUtils.toJSONString(result);
    }
}

6. login.jsp (HTML + jQuery)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head>
    <script src="<%=request.getContextPath()%>/static/bui/js/jquery-1.8.1.min.js"></script>
</head>
<body>
    username: <input type="text" id="username"><br><br>
    password: <input type="password" id="password"><br><br>
    <button id="loginbtn">登录</button>

    <script type="text/javascript">
    $("#loginbtn").click(function() {
        var param = {
            username: $("#username").val(),
            password: $("#password").val()
        };
        $.ajax({
            type: "post",
            url: "<%=request.getContextPath()%>/checkLogin.json",
            data: param,
            dataType: "json",
            success: function(data) {
                if (data.success == false) {
                    alert(data.errorMsg);
                } else {
                    window.location.href = "<%=request.getContextPath()%>/loginsuccess.jhtml";
                }
            },
            error: function() {
                alert("调用失败....");
            }
        });
    });
    </script>
</body>
</html>

7. Demonstration

The article includes screenshots showing the redirection to the login page when unauthenticated, the AJAX‑driven login failure and success messages, and the final page after a successful login.

8. Common Pitfall

When the AJAX request sets contentType: "application/json" , the controller cannot bind the username and password parameters; removing the explicit content type resolves the issue.

authenticationSpring MVCauthorizationApache ShiroJava security
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

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.