当前位置: 首页 > 工具软件 > CacheManager > 使用案例 >

使用‘net.sf.ehcache.CacheManager‘进行Shiro配置时报错

杜绍元
2023-12-01

注:作者水平有限,有错误请大佬及时评论纠正,谢谢!

1、首先说一下第一次报错:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-09-04 12:34:51.550 ERROR 8692 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field cacheManager in com.springbootdemo.common.service.common.EcacheService required a bean of type 'net.sf.ehcache.CacheManager' that could not be found.


Action:

Consider defining a bean of type 'net.sf.ehcache.CacheManager' in your configuration.

Disconnected from the target VM, address: '127.0.0.1:61743', transport: 'socket'

Process finished with exit code 1

此时未在"resources"下级目录中添加"ehcache.xml"文件,以及启动类中未加注解"@EnableCaching"。

        "@EnableCaching"详解

@EnableCaching注解是spring framework中的注解驱动的缓存管理功能。自spring版本3.1起加入了该注解。如果你使用了这个注解,那么你就不需要在XML文件中配置cache manager了。

当你在配置类(@Configuration)上使用@EnableCaching注解时,会触发一个post processor,这会扫描每一个spring bean,查看是否已经存在注解对应的缓存。如果找到了,就会自动创建一个代理拦截方法调用,使用缓存的bean执行处理。

        "ehcache.xml"

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <!--
       diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
       user.home – 用户主目录
       user.dir  – 用户当前工作目录
       java.io.tmpdir – 默认临时文件路径
     -->
    <diskStore path="java.io.tmpdir/springbootdemo"/>
    <!--
       defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
    <!--
      name:缓存名称。
      maxElementsInMemory:缓存最大数目
      maxElementsOnDisk:硬盘最大缓存个数。
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。
      overflowToDisk:是否保存到磁盘,当系统当机时
      timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
      timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
      diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
      diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
      clearOnFlush:内存数量最大时是否清除。
      memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
      FIFO,first in first out,这个是大家最熟的,先进先出。
      LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
      LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->
    <defaultCache
            eternal="false"
            maxElementsInMemory="16834"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="60"
            diskExpireThreadIntervalSeconds ="120"
    />

    <cache
            name="userLoginInfo"
            eternal="false"
            maxElementsInMemory="16834"
            overflowToDisk="true"
            diskPersistent="true"
            timeToIdleSeconds="43200"
            timeToLiveSeconds="0"
            maxElementsOnDisk = "0"
    />

</ehcache>

2、加上"ehcache.xml"以及"@EnableCaching"注解后,第二次报错:

Caused by: org.xml.sax.SAXException: null:39: Element <defaultCache> does not allow attribute "diskExpireThreadIntervalSeconds".
	at net.sf.ehcache.config.BeanHandler.setAttribute(BeanHandler.java:281) ~[ehcache-core-2.1.0.jar:na]
	at net.sf.ehcache.config.BeanHandler.startElement(BeanHandler.java:112) ~[ehcache-core-2.1.0.jar:na]
	at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source) ~[na:1.8.0_144]
	at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.parse(Unknown Source) ~[na:1.8.0_144]
	at javax.xml.parsers.SAXParser.parse(Unknown Source) ~[na:1.8.0_144]
	at net.sf.ehcache.config.ConfigurationFactory.parseConfiguration(ConfigurationFactory.java:155) ~[ehcache-core-2.1.0.jar:na]
	... 96 common frames omitted

Disconnected from the target VM, address: '127.0.0.1:61936', transport: 'socket'

Process finished with exit code 1

        查看后发现是"ehcache.xml"中的"diskExpireThreadIntervalSeconds"属性有问题,此时去掉这个属性以及属性值,再次尝试,登录正常,"CacheManager"         可正常使用。

3、CacheManager的使用

        引用"CacheManager"的maven

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
            <version>2.2.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.googlecode.ehcache-spring-annotations</groupId>
            <artifactId>ehcache-spring-annotations</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.10.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>

        自定义”EcacheService“,设置用户信息的存储

package com.springbootdemo.common.service.common;

import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
public class EcacheService {

    @Autowired
    private CacheManager cacheManager;

    public Object get(String names,Object key){
        Element element = cacheManager.getCache(names).get(key);
        if(element == null){
            return null;
        }
        return element.getObjectValue();
    }

    public String getString(String names,Object key){
        Element element = cacheManager.getCache(names).get(key);
        if(element == null){
            return null;
        }
        return element.getObjectValue().toString();
    }

    public <T> T get(String names,Object key,Class<T> clazz){
        Element element = cacheManager.getCache(names).get(key);
        if(element == null){
            return null;
        }
        return (T)element.getObjectValue();
    }

    public void set(String names,Object key, Object value){
        cacheManager.getCache(names).put(new Element(key,value));
    }
}

        "apiShrio"的调用

package com.springbootdemo.common.shiro;

import com.springbootdemo.common.base.Result;
import com.springbootdemo.common.constant.CacheKeys;
import com.springbootdemo.common.enums.ResultCode;
import com.springbootdemo.common.exception.ApiShiroException;
import com.springbootdemo.common.exception.CommonException;
import com.springbootdemo.common.pojo.po.SysUserDO;
import com.springbootdemo.common.service.common.EcacheService;
import com.springbootdemo.common.util.JwtUtils;
import org.apache.commons.lang3.StringUtils;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Set;

public class ApiShiroRealm extends AuthorizingRealm {

    protected static final Logger logger = LoggerFactory.getLogger(ApiShiroRealm.class);

    public static Set<String> ALL_PERMISSIONS_SET;

    public static final String[] DEFAULT_PERMISSIONS_ARRAY = {"add","update","view","opt","delete"};

    @Autowired
    private EcacheService ecacheService;

    @Override
    public String getName(){
        return "Realm";
    }

    @Override
    public boolean supports(AuthenticationToken token){
        return token != null && ApiShiroToken.class.isAssignableFrom(token.getClass());
    }

    /**
     * 登录认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        ApiShiroToken token = (ApiShiroToken) authenticationToken;
        try {
            if(StringUtils.isBlank(token.getToken())){
                throw new ApiShiroException(Result.fail(ResultCode.CODE_101,"token错误"));
            }
            Integer userId = JwtUtils.getUserId(token.getToken());
            if(userId == null){
                throw new ApiShiroException(Result.fail(ResultCode.CODE_101,"token解析错误"));
            }
            SimpleAuthenticationInfo authenticationInfo = (SimpleAuthenticationInfo) ecacheService.get(CacheKeys.USER_LOGIN_INFO,userId);
            if(authenticationInfo == null){
                throw new ApiShiroException(Result.fail(ResultCode.CODE_101,"token已过期"));
            }
            return authenticationInfo;
        }catch (Exception e){
            throw new CommonException(Result.fail(""),e);
        }
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        return ((ApiShiroUser)principal.getPrimaryPrincipal()).getAuthorizationInfo();
    }

    /**
     * api shiro 登录
     * @return
     */
    public ApiShiroUser apiShiroLogin(SysUserDO user, ApiShiroToken apiShiroToken, Set<String> roles, Set<String> permissions){
        //权限设置
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(permissions);

        //用户信息生成,token创建
        ApiShiroUser apiShiroUser = new ApiShiroUser();
        apiShiroUser.setUserId(user.getId());
        apiShiroUser.setUsername(user.getUsername());
        apiShiroUser.setRealName(user.getRealName());
        apiShiroUser.setAuthorizationInfo(authorizationInfo);
        apiShiroUser.setToken(JwtUtils.createToken(user.getId()));

        //shiro认证信息存储
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(apiShiroUser, apiShiroUser.getToken(), this.getName());
        ecacheService.set(CacheKeys.USER_LOGIN_INFO, user.getId(), authenticationInfo);
        return apiShiroUser;
    }

}

​​​​​​​

 类似资料: