Shiro 会话管理和加密

一. 会话管理


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 的核心方法包括:

  1. create(Session session): 创建会话,并将其持久化
  2. readSession(Serializable sessionId): 根据会话ID读取会话信息
  3. update(Session session): 更新会话信息(如更新超时时间或会话属性)
  4. 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 会话管理可以灵活使用:

  1. 分布式会话:在多节点应用中,通常使用 Redis、Memcached 等分布式缓存来实现会话共享Shiro 的 RedisSessionDAO 通过将会话信息存储到 Redis,以保证会话在多节点之间共享
  2. 会话超时:通过 session.setTimeout(long timeout) 设置会话超时时间(单位为毫秒),当会话达到设定时间未活动时会自动失效
  3. 会话事件监听: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>
<!--        &lt;!&ndash; 引入MyBatis-Plus动态数据源支持 &ndash;&gt;-->
<!--        <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);

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值