一. 会话管理
1. 会话相关API
Shiro 提供了丰富的会话管理接口以支持与 Web 环境无关的会话操作。Session 和 Subject 是 Shiro 会话管理的核心接口
Session:代表用户的一次会话。Session 接口提供了基础会话操作,如获取或设置会话属性、会话ID、超时时间等。其中的核心方法包括:
- getId(): 获取会话的唯一标识符
- getAttribute(Object key): 获取会话属性,用于存储用户或会话相关数据
- setAttribute(Object key, Object value): 设置会话属性
- removeAttribute(Object key): 删除指定的会话属性
- getTimeout() / setTimeout(long): 获取或设置会话超时时间,通常以毫秒为单位
- stop(): 终止会话,通常在用户注销或会话超时时调用
Subject:表示当前操作的用户或系统实体。Subject 的 getSession() 方法可以获取当前用户的会话,支持自动创建或获取已有会话。Subject.getSession(true) 会在没有会话时自动创建新会话,而 Subject.getSession(false) 则只获取已有会话,不会创建新会话。
2. 编写SessionDAO
package com.ty.springbootshiro.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.ty.springbootshiro.entity.Right;
import com.ty.springbootshiro.service.RoleService;
import jakarta.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* ShiroConfig
*
* @aurhor Administrator whs
* @since 2024/10/8
*/
@Configuration
public class ShiroConfig {
@Resource
private RoleService roleService;
//注入redis参数 ,从 pom.xml 文件中获取
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Value("${spring.data.redis.password}")
private String password;
@Value("${spring.data.redis.connect-timeout}")
private int timeout;
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect() { // thymeleaf 页面上使用 shiro 标签
return new ShiroDialect();
}
/**
* 开启Shiro注解
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启 aop 注解支持
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 创建 redisManager
* @return
*/
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
// redisManager.setHost(host);
// redisManager.setPort(port);
// redisManager.setDatabase(port);
redisManager.setHost(host + ":" + port); // #shiro-redis v3.3.1
redisManager.setPassword(password);
redisManager.setTimeout(timeout);
return redisManager;
}
/**
* 缓存管理器
*/
@Bean
public CacheManager shiroCacheManager() {
RedisCacheManager cacheManager = new RedisCacheManager();
cacheManager.setRedisManager(redisManager());
// 缓存名称
cacheManager.setPrincipalIdFieldName("usrName");
// 缓存有效时间
cacheManager.setExpire(1800);
return cacheManager;
}
/**
* 创建 RedisCacheManager 注入 RedisManager
* @return
*/
@Bean
public RedisCacheManager cacheManager(){
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
// 缓存名称
redisCacheManager.setPrincipalIdFieldName("usrName");
// 缓存有效时间
redisCacheManager.setExpire(timeout);
return redisCacheManager;
}
/**
* 会话操作
* 创建 RedisSessionDAO 注入 RedisManager
* @return
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* 会话管理
* 创建 DefaultWebSessionManager 注入 RedisSessionDAO
* @return
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
/**
* shiro 提供的 CredentialsMatcher 散列实现 HashedCredentialsMatcher 用于密码验证
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用 md5 算法进行加密
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 设置散列次数 : 意为加密几次
hashedCredentialsMatcher.setHashIterations(520);
return hashedCredentialsMatcher;
}
@Bean
public MyShiroRealm myShiroRealm() { // 自定义Realm
MyShiroRealm myShiroRealm = new MyShiroRealm();
// 设置启用缓存,并设置缓存名称
myShiroRealm.setCachingEnabled(true);
myShiroRealm.setAuthenticationCachingEnabled(true);
myShiroRealm.setAuthenticationCacheName("authenticationCache");
// 设置凭证(密码)匹配器
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
@Bean
public SecurityManager securityManager() { // 安全管理器 SecurityManager
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//注入 Realm
securityManager.setRealm(myShiroRealm());
SecurityUtils.setSecurityManager(securityManager);
// 注入缓存管理器
securityManager.setCacheManager(cacheManager());
// 注入会话管理器
securityManager.setSessionManager(sessionManager());
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { // 过滤器 权限验证
ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
// 注入securityManager
shiroFilterFactory.setSecurityManager(securityManager);
// 权限验证 : 使用 Filter 控制资源(URL)的访问
shiroFilterFactory.setLoginUrl("/index");
shiroFilterFactory.setSuccessUrl("/main");
shiroFilterFactory.setUnauthorizedUrl("/403"); //没有权限跳转到403
//
// Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); // 必须使用LinkedHashMap 有序集合
//
// // 配置可以匿名访问资源的url: 静态资源
// filterChainDefinitionMap.put("/css/**", "anon");
// filterChainDefinitionMap.put("/fonts/**", "anon");
// filterChainDefinitionMap.put("/images/**", "anon");
// filterChainDefinitionMap.put("/js/**", "anon");
// filterChainDefinitionMap.put("/localcss/**", "anon");
// filterChainDefinitionMap.put("/localjs/**", "anon");
//
// filterChainDefinitionMap.put("/login/**", "anon");
// filterChainDefinitionMap.put("/logout/**", "anon"); // 注销过滤器 , 自动注销
//
// // 配置需要特定权限才能范文的资源的url
//// // 静态授权: 包括全部需要特定权限才能访问的资源URl
//// filterChainDefinitionMap.put("/user/list", "perms[用户列表]");
//// filterChainDefinitionMap.put("/user/save", "perms[用户添加]");
//// filterChainDefinitionMap.put("/user/edit", "perms[用户编辑]");
//// filterChainDefinitionMap.put("/user/del/", "perms[用户删除]");
//
//
//
//// // 动态授权
// List<Right> rights = roleService.findAllRights();
// for (Right right : rights) {
// if (right.getRightUrl()!=null && right.getRightUrl().trim().equals("")) { // .startsWith("/user/")
// filterChainDefinitionMap.put(right.getRightUrl(), "perms["+right.getRightUrl()+"]");
// }
// }
//
//
// // 配置认证访问 : 其他资源URl必须认证通过才能访问
// filterChainDefinitionMap.put("/**", "authc"); // 必须放在过滤器链的最后面
//
// shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactory;
}
}
在 Shiro 中,SessionDAO 负责会话的持久化与读取,它支持将会话信息存储到不同介质中(如内存、数据库、文件或分布式缓存)。Shiro 默认实现了多个 SessionDAO,常用的包括:
MemorySessionDAO:将会话存储在内存中,适合小规模应用,但不适合分布式环境
EnterpriseCacheSessionDAO:结合缓存管理器,将会话存储在缓存中,支持更高的性能和扩展性
自定义 SessionDAO:可以实现分布式存储支持(如 Redis、MySQL),在分布式环境中使用较多
SessionDAO 的核心方法包括:
- create(Session session): 创建会话,并将其持久化
- readSession(Serializable sessionId): 根据会话ID读取会话信息
- update(Session session): 更新会话信息(如更新超时时间或会话属性)
- delete(Session session): 删除会话,通常在会话超时或用户注销时调用
3.会话使用
package com.ty.springbootshiro.config;
import com.ty.springbootshiro.entity.Right;
import com.ty.springbootshiro.entity.Role;
import com.ty.springbootshiro.entity.User;
import com.ty.springbootshiro.service.RoleService;
import com.ty.springbootshiro.service.UserService;
import jakarta.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.lang.util.ByteSource;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.List;
import java.util.Set;
/**
* MyShiroRealm
*
* @aurhor Administrator whs
* @since 2024/10/8
*/
public class MyShiroRealm extends AuthorizingRealm { // 安全数据源
@Resource
// @Lazy
private UserService userService;
@Resource
private RoleService roleService;
@Resource
// @Lazy
private StringRedisTemplate stringRedisTemplate;
private String SHIRO_LOGIN_COUNT = "shiro_login_count_"; // 登录计数
private String SHIRO_IS_LOCK = "shiro_is_lock_"; // 锁定用户登录
// 清空当前认证用户权限缓存
public void clearMyCachedAuthorizationInfo(PrincipalCollection principals) {
clearMyCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
}
// 清空所有用户权限缓存
public void clearAllCachedAuthorizationInfo(){
if (this.isAuthorizationCachingEnabled()){ // 权限缓存是否可用
Cache<Object,AuthorizationInfo> cathe = null;
CacheManager cacheManager = this.getCacheManager();
if (cacheManager != null) {
String cacheName = this.getAuthenticationCacheName(); // 获得缓存名称
cathe = cacheManager.getCache(cacheName); // 获得权限缓存
}
if (cathe != null) {
cathe.clear(); // 清空
}
}
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("调用 MyShiroRealm 的 doGetAuthenticationInfo 获取身份信息");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String usrName = token.getUsername();
System.out.println("usrName -----------------> " +usrName);
// // 每次访问,登录次数加1
// ValueOperations<String,String> operations = stringRedisTemplate.opsForValue();
// operations.increment(SHIRO_LOGIN_COUNT + usrName, 1);
// // 计数大于5时,设置用户锁定一小时,清空登录计数
// if (Integer.parseInt(operations.get(SHIRO_LOGIN_COUNT + usrName)) > 5 ){
// operations.set(SHIRO_IS_LOCK + usrName , "LOCK");
// stringRedisTemplate.expire(SHIRO_LOGIN_COUNT + usrName,1, TimeUnit.HOURS);
// stringRedisTemplate.delete(SHIRO_LOGIN_COUNT + usrName); // 清空登录计数
// }
// if ("LOCK".equals(operations.get(SHIRO_IS_LOCK + usrName))){
// throw new DisabledAccountException();
// }
User user = userService.getUserByName(usrName);
System.out.println("user ------------------>" +user);
if (user == null) {
throw new AuthenticationException();//账号错误
}
if (user.getUsrFlag() == null || user.getUsrFlag().intValue() == 0) {
throw new LockedAccountException();//账号被禁用
}
// 修改这里,使用 getName() 方法获取 Realm 的名称,确保与设置 SecurityManager 时一致
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
user,
user.getUsrPassword(),
ByteSource.Util.bytes("离别情诗雨"), // 加密所使用的 salt(盐)
getName());
System.out.println("info ---------------->" + info);
return info;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("调试myShiroRealm.doGetAuthorizationInfo()获取权限信息");
User sysUser = (User) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addRole(sysUser.getRole().getRoleName());
//
// info.addStringPermission("用户列表"); // 所有用户拥有“用户列表”权限
// if ("管理员".equals(sysUser.getRole().getRoleName())){ // 管理员有 增删改的权限
// info.addStringPermission("用户添加");
// info.addStringPermission("用户编辑");
// info.addStringPermission("用户删除");
// }
// 动态授权
// Role role = sysUser.getRole();
Role role = roleService.findRoleByUsers(sysUser);
System.out.println("role ----------> " + role);
if (role != null) {
info.addRole(sysUser.getRole().getRoleName());
// Set<Right> rights = role.getRights();
List<Right> rights = roleService.findRightsByRole(role);
if (rights != null && rights.size() > 0) {
for (Right right : rights) {
info.addStringPermission(right.getRightCode());
}
}
}else {
System.out.println("没有权限奥~~");
}
return info;
}
}
在实际应用中,Shiro 会话管理可以灵活使用:
- 分布式会话:在多节点应用中,通常使用 Redis、Memcached 等分布式缓存来实现会话共享Shiro 的 RedisSessionDAO 通过将会话信息存储到 Redis,以保证会话在多节点之间共享
- 会话超时:通过 session.setTimeout(long timeout) 设置会话超时时间(单位为毫秒),当会话达到设定时间未活动时会自动失效
- 会话事件监听:Shiro 支持会话监听器(SessionListener),可以在会话创建、销毁、超时时执行自定义操作,如记录日志或发送通知
IndexController:
package com.ty.springbootshiro.controller;
import com.ty.springbootshiro.config.MyShiroRealm;
import com.ty.springbootshiro.entity.Right;
import com.ty.springbootshiro.entity.Role;
import com.ty.springbootshiro.entity.User;
import com.ty.springbootshiro.service.RoleService;
import com.ty.springbootshiro.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpSession;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.subject.Subject;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
/**
* IndexConfig
*
* @aurhor Administrator whs
* @since 2024/9/13
*/
@Controller
public class IndexController {
@Resource
private StringRedisTemplate stringRedisTemplate;
// Redis 中数据的key前缀
private String SHIRO_LOGIN_COUNT = "shiro_login_count_"; // 登录计数
@Resource
private UserService userService;
@Resource
private RoleService roleService;
/**
* 去登录页
*/
@GetMapping("/login")
public String toLogin() {
return "login";
}
@RequestMapping("/main")
public String main() {
return "main";
}
@RequestMapping("/403")
public String unauthorized() {
return "403";
}
@RequestMapping("/login")
public String login(String usrName, String usrPassword, Model model, HttpSession session) {
try {
// 加密处理
// usrPassword = userService.encryptPassword(usrPassword);
// System.out.println("usrPassword-------------------> " + usrPassword);
UsernamePasswordToken token = new UsernamePasswordToken(usrName, usrPassword);
Subject subject = SecurityUtils.getSubject();
subject.login(token); // 认证登录
// 如果认证(登录)成功,清空登录计数
stringRedisTemplate.delete(SHIRO_LOGIN_COUNT + usrName);
User user = (User) subject.getPrincipal();
System.out.println("user ------ > " + user);
session.setAttribute("loginUser", user);
// 获取权限
Role role = user.getRole();
List<Right> rights = roleService.findRightsByRole(role);
role.getRights().addAll(rights);
model.addAttribute("rights", rights);
session.setAttribute("loginUser", user);
// 清空权限缓存
RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager();
MyShiroRealm realm = (MyShiroRealm) rsm.getRealms().iterator().next();
realm.clearMyCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
return "redirect:/main";
}catch (UnknownAccountException | IncorrectCredentialsException e) {
model.addAttribute("msg", "用户名或密码错误,登录失败!");
return "login";
}catch (LockedAccountException e) {
model.addAttribute("msg", "用户被禁用,登录失败!");
return "login";
}catch (AuthenticationException e) {
model.addAttribute("msg", "认证异常,登录失败!");
return "login";
}
}
@RequestMapping("/logout")
public String logout(HttpSession session) {
session.removeAttribute("loginUser");
SecurityUtils.getSubject().logout(); // shiro 注销
return "redirect:/login"; //重定向 保证删除Cookie
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ty</groupId>
<artifactId>SpringBootShiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringBootShiro</name>
<description>SpringBootShiro</description>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<!-- 引入Web支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入Thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 单独引入Thymeleaf的布局依赖 -->
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
<!-- 引入MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 引入MyBatis-Plus支持(不需要再引入MyBatis包) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
<!-- 引入Druid数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.20</version>
</dependency>
<!-- <!– 引入MyBatis-Plus动态数据源支持 –>-->
<!-- <dependency>-->
<!-- <groupId>com.baomidou</groupId>-->
<!-- <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>-->
<!-- <version>4.1.2</version>-->
<!-- </dependency>-->
<!-- 引入Jedis操作Redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.8.0</version>
</dependency>
<!-- 引入池化技术支持 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- 引入SpringBoot操作Redis所需依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 引入Shiro安全框架Spring适配依赖,Shiro框架针对新版本SpringBoot 3.0.0做了适配 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<classifier>jakarta</classifier>
<version>2.0.1</version>
<!-- 排除仍使用了javax.servlet的依赖shiro-core、shiro-web包 -->
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入适配了SpringBoot 3.0.0新版本的shiro-core依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<classifier>jakarta</classifier>
<version>2.0.1</version>
</dependency>
<!-- 引入适配了SpringBoot 3.0.0新版本的shiro-web依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<classifier>jakarta</classifier>
<version>2.0.1</version>
<!-- 排除仍使用了javax.servlet的依赖shiro-core包 -->
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入Thymeleaf对Shiro标签的支持 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!-- Shiro配置Redis缓存管理 -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.3.1</version>
<!-- 排除自带的Jedis包 -->
<exclusions>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Shiro安全框架需要用到老版本的ServletAPI包支持 -->
<!-- <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency> -->
<!-- 引入FastJSON -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<!-- 引入HuTool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.24</version>
</dependency>
<!-- 引入Lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 引入Slf4j日志依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- 引入SpringBoot测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- SpringBoot支持热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
二. 缓存
(1) 问题分析
在高并发系统中,频繁的数据查询会显著增加系统负载。例如,Shiro 在会话验证、权限检查等场景下频繁访问用户或权限数据,容易造成数据库压力增大,从而影响系统性能
(2) 解决办法
Shiro 通过 CacheManager 进行缓存管理,可以整合第三方缓存系统(如 EhCache、Redis)。利用缓存可以有效减少对数据库的访问次数,提高系统响应速度。Shiro 的 CacheManager 支持缓存用户会话、认证信息、权限数据等。常见的缓存实现包括:
- EhCacheManager:基于 EhCache 的内存缓存,适合小型应用,支持本地缓存。
- RedisCacheManager:基于 Redis 的分布式缓存管理,适合大型分布式应用,支持缓存共享。
- 自定义 CacheManager:可根据需求自定义缓存实现,支持灵活扩展
可以通过 XML 或 Java 配置将缓存管理器引入 Shiro,配置方法如下:
EhCache 配置:
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:缓存配置文件名.xml");
securityManager.setCacheManager(cacheManager);
Redis 配置示例:
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager);
securityManager.setCacheManager(redisCacheManager);
618

被折叠的 条评论
为什么被折叠?



