springboot + shiro 配置 ehcache 缓存

背景:

       shiro 是支持缓存功能的,它可以对用户的授权数据认证数据进行缓存,使得用户不必每次认证或授权时都去查询数据库,今天讲下如何将 ehcache 缓存整合到 shiro 中,后续再讲下如何将 redis 整合到 shiro 中。

添加依赖:

    <dependency>
		<groupId>org.apache.shiro</groupId>
		<artifactId>shiro-ehcache</artifactId>
		<version>1.6.0</version>
	</dependency>

配置ShiroConfig:

       首先在 ShiroConfig 中添加 shiro 的缓存管理器 EhCacheManager ,并将缓存管理器添加到 SecurityManager 中,最后在 CustomRealm 中配置开启缓存,如下所示:

    /**
	 * shiro缓存管理器;
	 * 需要添加到securityManager中
	 * @return
	 */
	@Bean
	public EhCacheManager ehCacheManager(){
	    EhCacheManager cacheManager = new EhCacheManager();
	    cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
	    return cacheManager;
	}
    @Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(myShiroRealm());
		// 将 CookieRememberMeManager 注入到 SecurityManager 中,否则不会生效
		securityManager.setRememberMeManager(rememberMeManager());
		// 将 sessionManager 注入到 SecurityManager 中,否则不会生效
		securityManager.setSessionManager(sessionManager());
		// 将 EhCacheManager 注入到 SecurityManager 中,否则不会生效
		securityManager.setCacheManager(ehCacheManager());
		return securityManager;
	}
    @Bean
	public CustomRealm myShiroRealm() {
		CustomRealm customRealm = new CustomRealm();
		// 告诉realm,使用credentialsMatcher加密算法类来验证密文
		customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		/* 开启支持缓存,需要配置如下几个参数 */
		customRealm.setCachingEnabled(true);
	    // 启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
		customRealm.setAuthenticationCachingEnabled(true);
	    // 缓存AuthenticationInfo信息的缓存名称 在 ehcache-shiro.xml 中有对应缓存的配置
		customRealm.setAuthenticationCacheName("authenticationCache");
	    // 启用授权缓存,即缓存AuthorizationInfo信息,默认false
		customRealm.setAuthorizationCachingEnabled(true);
	    // 缓存AuthorizationInfo 信息的缓存名称  在 ehcache-shiro.xml 中有对应缓存的配置
		customRealm.setAuthorizationCacheName("authorizationCache");
		return customRealm;
	}

添加配置文件:

       需要在 resources 文件夹下添加 ehcache-shiro.xml 的配置文件,内容如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">

    <!--
        缓存对象存放路径
        java.io.tmpdir:默认的临时文件存放路径。
        user.home:用户的主目录。
        user.dir:用户的当前工作目录,即当前程序所对应的工作路径。
        其它通过命令行指定的系统属性,如“java –DdiskStore.path=D:\\abc ……”。
    -->
    <diskStore path="java.io.tmpdir"/>

    <!--

       name:缓存名称。
       maxElementsOnDisk:硬盘最大缓存个数。0表示不限制
       maxEntriesLocalHeap:指定允许在内存中存放元素的最大数量,0表示不限制。
       maxBytesLocalDisk:指定当前缓存能够使用的硬盘的最大字节数,其值可以是数字加单位,单位可以是K、M或者G,不区分大小写,
                          如:30G。当在CacheManager级别指定了该属性后,Cache级别也可以用百分比来表示,
                          如:60%,表示最多使用CacheManager级别指定硬盘容量的60%。该属性也可以在运行期指定。当指定了该属性后会隐式的使当前Cache的overflowToDisk为true。
       maxEntriesInCache:指定缓存中允许存放元素的最大数量。这个属性也可以在运行期动态修改。但是这个属性只对Terracotta分布式缓存有用。
       maxBytesLocalHeap:指定当前缓存能够使用的堆内存的最大字节数,其值的设置规则跟maxBytesLocalDisk是一样的。
       maxBytesLocalOffHeap:指定当前Cache允许使用的非堆内存的最大字节数。当指定了该属性后,会使当前Cache的overflowToOffHeap的值变为true,
                             如果我们需要关闭overflowToOffHeap,那么我们需要显示的指定overflowToOffHeap的值为false。
       overflowToDisk:boolean类型,默认为false。当内存里面的缓存已经达到预设的上限时是否允许将按驱除策略驱除的元素保存在硬盘上,默认是LRU(最近最少使用)。
                      当指定为false的时候表示缓存信息不会保存到磁盘上,只会保存在内存中。
                      该属性现在已经废弃,推荐使用cache元素的子元素persistence来代替,如:<persistence strategy=”localTempSwap”/>。
       diskSpoolBufferSizeMB:当往磁盘上写入缓存信息时缓冲区的大小,单位是MB,默认是30。
       overflowToOffHeap:boolean类型,默认为false。表示是否允许Cache使用非堆内存进行存储,非堆内存是不受Java GC影响的。该属性只对企业版Ehcache有用。
       copyOnRead:当指定该属性为true时,我们在从Cache中读数据时取到的是Cache中对应元素的一个copy副本,而不是对应的一个引用。默认为false。
       copyOnWrite:当指定该属性为true时,我们在往Cache中写入数据时用的是原对象的一个copy副本,而不是对应的一个引用。默认为false。
       timeToIdleSeconds:单位是秒,表示一个元素所允许闲置的最大时间,也就是说一个元素在不被请求的情况下允许在缓存中待的最大时间。默认是0,表示不限制。
       timeToLiveSeconds:单位是秒,表示无论一个元素闲置与否,其允许在Cache中存在的最大时间。默认是0,表示不限制。
       eternal:boolean类型,表示是否永恒,默认为false。如果设为true,将忽略timeToIdleSeconds和timeToLiveSeconds,Cache内的元素永远都不会过期,也就不会因为元素的过期而被清除了。
       diskExpiryThreadIntervalSeconds :单位是秒,表示多久检查元素是否过期的线程多久运行一次,默认是120秒。
       clearOnFlush:boolean类型。表示在调用Cache的flush方法时是否要清空MemoryStore。默认为true。
       diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       maxElementsInMemory:缓存最大数目
       memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
            memoryStoreEvictionPolicy:
               Ehcache的三种清空策略;
               FIFO,first in first out,这个是大家最熟的,先进先出。
               LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
               LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
    -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="0"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />

    <!-- 授权缓存 -->
    <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <!-- 认证缓存 -->
    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

</ehcache>

配置日志输出验证:      

       在 CustomRealm 类的 doGetAuthorizationInfo() 方法中添加打印,或者直接看控制台 sql 打印也行,如下所示:

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		System.out.println("开始查询数据库");
		// 获取登录用户名
		String name = (String)principalCollection.getPrimaryPrincipal();
		// 查询用户名称
		User user = userService.selectByUserName(name);
		// 添加角色和权限
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		List<String> roleNameList = new ArrayList<>();
		List<String> permissionNameList = new ArrayList<>();

		for (Role role : user.getRoles()) {
			roleNameList.add(role.getRoleName());
			for (Permission permission : role.getPermissions()) {
				permissionNameList.add(role.getRoleName()+":"+permission.getPermissionName());
			}
		}
		// 添加角色
		simpleAuthorizationInfo.addRoles(roleNameList);
		// 添加权限
		simpleAuthorizationInfo.addStringPermissions(permissionNameList);
		System.out.println("查询数据库结束");
		return simpleAuthorizationInfo;
	}

       启动项目,使用 zhangsan 账号登录,第一次访问,查看控制台,有日志输出,如下所示,下面只截取了一部分的日志输出,再次访问将不再打印输出,证明缓存生效了。上面的缓存配置时间配置为永久,请根据需求自己更改值来进行测试。 在 CustomRealm 中对用户信息角色信息权限信息的查询,如果需要添加缓存请自行处理。

更改权限:

       当用户的权限发生改变时我们该如何处理?上面的代码中已经启用了缓存,第一次请求走数据库查询,后续请求将直接查询 ehcache 缓存,假如这个时候在权限控制台分配了某个权限给某个角色,那么拥有这个角色的所有用户在下次请求之前都需要从数据库查询最新的权限信息。所以我们需要清理缓存。

       首先在 CustomRealm 中添加下面的方法用于清理缓存,如下所示:

/**
	 * 重写方法,清除当前用户的的 授权缓存
	 * @param principals
	 */
	@Override
	public void clearCachedAuthorizationInfo(PrincipalCollection principal) {
		 super.clearCachedAuthorizationInfo(principal);
	}
	/**
	 * 重写方法,清除当前用户的 认证缓存
	 * @param principals
	 */
	@Override
	public void clearCachedAuthenticationInfo(PrincipalCollection principal) {
		super.clearCachedAuthenticationInfo(principal);
	}

	/**
	 *  重写方法,清除当前用户的 认证缓存和授权缓存
	 * */
	@Override
	public void clearCache(PrincipalCollection principals) {
		super.clearCache(principals);
	}

	/**
	 * 自定义方法:清除所有用户的 授权缓存
	 */
	public void clearAllCachedAuthorizationInfo() {
		getAuthorizationCache().clear();
	}

	/**
	 * 自定义方法:清除所有用户的 认证缓存
	 */
	public void clearAllCachedAuthenticationInfo() {
		getAuthenticationCache().clear();
	}

	/**
	 * 自定义方法:清除所有用户的  认证缓存  和 授权缓存
	 */
	public void clearAllCache() {
		clearAllCachedAuthenticationInfo();
		clearAllCachedAuthorizationInfo();
	}

        然后在 ShiroConfig 中添加 MethodInvokingFactoryBean ,代码如下所示:

    /**
	 * 让某个实例的某个方法的返回值注入为Bean的实例
	 * Spring静态注入
	 * @return
	 */
	@Bean
	public MethodInvokingFactoryBean getMethodInvokingFactoryBean(){
	    MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
	    factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
	    factoryBean.setArguments(new Object[]{securityManager()});
	    return factoryBean;
	}

测试:

       在 LoginController 中添加如下方法用于清除缓存,在添加或者删除权限的时候,都需要清除缓存。

/**
     * 给admin用户添加 userInfo:del 权限
     * @param model
     * @return
     */
    @RequestMapping(value = "/addRoleIds",method = RequestMethod.GET)
    @ResponseBody
    public String addPermission(String userName) {
        //在sys_role_permission 表中  将 删除的权限 关联到admin用户所在的角色
    	User user = new User();
    	user.setUserName(userName);
    	user.setRoleIds("1,2");
    	userService.updateRoleIds(user);

        //添加成功之后 清除缓存
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager)SecurityUtils.getSecurityManager();
        CustomRealm shiroRealm = (CustomRealm) securityManager.getRealms().iterator().next();
        // 清除所有用户权限相关的缓存
        // shiroRealm.clearAllCache();
        // 清除当前登录用户的缓存
        shiroRealm.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
        return "给admin用户添加 userInfo:del 权限成功";
    }
    /**
     * 删除admin用户 userInfo:del 权限
     * @param model
     * @return
     */
    @RequestMapping(value = "/delRoleIds",method = RequestMethod.GET)
    @ResponseBody
    public String delPermission(String userName) {
        //在sys_role_permission 表中  将 删除的权限 关联到admin用户所在的角色
    	User user = new User();
    	user.setUserName(userName);
    	user.setRoleIds("1");
    	userService.updateRoleIds(user);
    	
        //添加成功之后 清除缓存
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager)SecurityUtils.getSecurityManager();
        CustomRealm shiroRealm = (CustomRealm) securityManager.getRealms().iterator().next();
        // 清除所有用户权限相关的缓存
        // shiroRealm.clearAllCache();
        // 清除当前登录用户的缓存
        shiroRealm.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
        return "删除admin用户userInfo:del 权限成功";

    }

       1、用两个浏览器分别用不同的用户登录到系统中,然后刷新界面,我们发现,无论怎么刷新,都不用有日志输出,因为此时查询的时缓存。

       2、点击界面的新增按钮,触发后台的 addRoleIds 方法,然后再刷新界面,我们发现此时后台有日志输出,即查询的数据是数据库里面的数据。

       3、点击界面的删除按钮,触发后台的 delRoleIds 方法,然后再刷新界面,我们发现此时后台有日志输出,即查询的数据是数据库里面的数据。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: springboot+shiro+jwt 是一种常见的后端技术组合,其中 springboot 是一个基于 Spring 框架的快速开发框架,shiro 是一个安全框架,用于身份验证、授权和加密等功能,jwt 是一种基于 token 的身份验证机制,可以用于前后端分离的应用中。这种技术组合可以帮助开发者快速搭建安全可靠的后端服务。 ### 回答2: Springboot是一个开源的Java开发框架,是基于Spring框架的远程服务器控制技术方案,是现代化的全栈框架,集成丰富,提供了大量的开箱即用的组件,可以大大简化开发人员的开发工作。 Shiro是一个强大的轻量级安全框架,支持用户身份识别、密码加密、权限控制、会话管理等一系列安全功能,被广泛应用于JavaWeb开发中。 JWT(JSON Web Token)是一种开放标准(RFC 7519),定义了一种简洁的、自包含的方式,用于通信双方之间以JSON对象的形式安全地传递信息。JWT可以用于状态管理和用户身份认证等场景。 在使用SpringBoot开发Web应用过程中,Shiro与JWT可以同时用于用户身份认证和权限控制。Shiro提供了一系列的身份识别、密码加密、权限控制、会话管理等功能,而JWT则可以实现无状态的身份认证。使用Shiro和JWT,可以有效地保护Web应用的安全,避免被恶意攻击者利用。 具体而言,使用Shiro和JWT可以将用户认证的主要逻辑统一在一起,实现更加优雅的认证和授权过程。同时,这样的组合也可以避免一些常见的安全漏洞,比如会话劫持、XSS攻击、CSRF等。 在实际开发中,使用SpringBoot Shiro JWT可以方便地进行二次开发,进一步优化开发成本和提高开发效率。同时,使用这个组合可以让开发者更好地专注于业务逻辑的开发,而无需关注安全问题,从而提高开发质量和开发人员的工作效率。 ### 回答3: Spring Boot是一种基于Spring框架的快速开发微服务的工具,能够使开发者可以快速构建基于Spring的应用程序。而Shiro是一个强大易用的Java安全框架,可用于身份验证、权限控制、加密等。JWT(JSON Web Token)是一种基于JSON的安全令牌,可用于在客户端和服务器之间传递信息。 在使用Spring Boot开发Web应用程序时,通常需要进行用户身份验证和访问控制,这时候就可以使用Shiro来实现这些功能。同时,由于Web应用程序需要跨域访问,因此使用JWT可以方便地实现身份验证和授权的管理。 在使用Spring BootShiro时,可以使用JWT作为身份验证和授权的管理工具。此时,需要进行以下几个步骤: 1.添加Shiro和JWT的依赖 在Spring Boot项目中,可以通过Maven或Gradle等工具添加Shiro和JWT的依赖。例如,可以添加以下依赖: <!-- Shiro依赖 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.7.0</version> </dependency> <!-- JWT依赖 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> 2.配置ShiroSpring Boot项目中,可以通过在application.properties或application.yml文件中进行Shiro配置。例如,可以配置以下内容: # Shiro配置 shiro: user: loginUrl: /login # 登录页面URL jwt: secret: my_secret # JWT加密密钥 expiration: 1800000 # JWT过期时间,单位为毫秒,默认30分钟 3.创建Shiro的Realm 在Shiro中,Realm是用于从应用程序中获取安全数据(如用户、角色和权限)的组件。因此,需要创建Shiro的Realm,用于管理用户的认证和授权。 例如,可以创建一个自定义的Realm,其中包括从数据库中获取用户和角色的方法: public class MyRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 认证,验证用户身份 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); User user = userService.findByUsername(username); if (user == null) { throw new UnknownAccountException("用户名或密码错误"); } String password = user.getPassword(); return new SimpleAuthenticationInfo(user.getUsername(), password, getName()); } /** * 授权,验证用户的访问权限 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); User user = userService.findByUsername(username); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Set<String> roles = user.getRoles(); authorizationInfo.setRoles(roles); Set<String> permissions = user.getPermissions(); authorizationInfo.setStringPermissions(permissions); return authorizationInfo; } } 4.使用JWT进行身份验证和授权管理 在Spring Boot应用程序中,使用Shiro和JWT来进行身份验证和授权管理的流程大致如下: 4.1 前端用户进行登录操作,将用户名和密码发送到后台服务。 4.2 后台服务进行身份验证,将用户身份信息生成JWT并返回给前端。 4.3 前端保存JWT,并在后续的请求中将其发送到后台服务。 4.4 后台服务验证JWT的有效性,并根据用户的角色和权限信息进行访问控制。 综上所述,Spring BootShiro和JWT可以很好地配合使用,实现Web应用程序的身份验证和授权管理。这种组合方案可以提高开发效率和系统安全性,同时也适用于微服务架构中对身份验证和授权的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐的小三菊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值