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