Spring boot中Shiro和Redis集成后,Spring的@cacheble注解无效? 财富值80

2016-10-09 07:26发布

Spring boot中Shiro和Redis集成后,Spring的@cacheble注解无效。

情况1:Spring boot和Redis集成,@cacheble可用,会把缓存数据写入Redis。
在情况1的前提下:
情况2:Spring boot和Shiro集成,然后用Spring cache抽象出cachemanager,作为Shiro的缓存。控制台未报错,Shiro的认证信息会存入Redis,但出现@cacheble注解无效,即@Cacheble注解的方法的返回值未存入Redis。
在情况2的前提下:
情况3:将Shiro的@Configuration注解去掉,即不用Shiro,@Cacheble可用。

Redis配置清单:

import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;  import javax.annotation.Resource; import java.lang.reflect.Method; @Configuration @EnableCaching public class RedisCacheConfig extends CachingConfigurerSupport {      /**      * 通过Spring进行注入,参数在application.properties进行配置{RedisProperties};      */     @Resource     private JedisConnectionFactory factory;      @Bean     @Override     public CacheManager cacheManager() {         System.out.println("RedisCacheConfig.cacheManager : 实例化");         RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate());         cacheManager.setDefaultExpiration(60 * 30); // 30min         return cacheManager;     }      @Bean     public RedisTemplate<Object, Object> redisTemplate() {         System.out.println("RedisCacheConfig.redisTemplate : 实例化");         RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();         redisTemplate.setConnectionFactory(factory);          redisTemplate.setKeySerializer(new GenericJackson2JsonRedisSerializer());         // redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());          return redisTemplate;     }      /**      * 自定义key.      * 此方法将会根据完全限定类名+方法名+所有参数的值生成唯一的一个key,即使@Cacheable中的value属性一样,key也会不一样。      */     @Bean     @Override     public KeyGenerator keyGenerator() {         System.out.println("RedisCacheConfig.keyGenerator : 实例化");         return new KeyGenerator() {             @Override             public Object generate(Object o, Method method, Object... objects) {                 // This will generate a unique key of the class name, the method name                 // and all method parameters appended.                 StringBuilder sb = new StringBuilder();                 sb.append(o.getClass().getName());                 sb.append(method.getName());                 for (Object obj : objects) {                     sb.append(obj.toString());                 }                 System.out.println("RedisCacheConfig : keyGenerator <== " + sb.toString());                 return sb.toString();             }         };     }      @Bean     @Override     public CacheResolver cacheResolver() {         return new SimpleCacheResolver(cacheManager());     }      @Bean     @Override     public CacheErrorHandler errorHandler() {         // 用于捕获从Cache中进行CRUD时的异常的回调处理器。         return new SimpleCacheErrorHandler();     }

缓存配置:

import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.apache.shiro.cache.CacheManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.support.SimpleValueWrapper; import org.springframework.stereotype.Component;  import javax.annotation.Resource; import java.util.Collection; import java.util.Set; @Component public class SpringCacheManagerWrapper implements CacheManager {      public static final Logger log = LoggerFactory.getLogger(SpringCacheManagerWrapper.class);      @Resource     private org.springframework.cache.CacheManager springCacheManger;      @Override     public <K, V> Cache<K, V> getCache(String name) throws CacheException {         log.debug("getCache: springCacheManger <== " + springCacheManger);         org.springframework.cache.Cache springCache = springCacheManger.getCache(name);         return new SpringCacheWrapper<K, V>(springCache);     }      private class SpringCacheWrapper<K, V> implements Cache<K, V> {         private org.springframework.cache.Cache springCache;         public SpringCacheWrapper(org.springframework.cache.Cache springCache) {             this.springCache = springCache;         }          @Override         public V get(K key) throws CacheException {             Object value = springCache.get(key);             if (value instanceof SimpleValueWrapper) {                 return (V) ((SimpleValueWrapper)value).get();             }             return (V) value;         }          @Override         public V put(K key, V value) throws CacheException {             springCache.put(key, value);             return value;         }          @Override         public V remove(K key) throws CacheException {             Object value = get(key);             springCache.evict(key);             return (V) value;         }          @Override         public void clear() throws CacheException {             springCache.clear();         }          @Override         public int size() {             throw new UnsupportedOperationException("invoke spring cache abstract size method not supported");          }          @Override         public Set<K> keys() {             throw new UnsupportedOperationException("invoke spring cache abstract keys method not supported");          }          @Override         public Collection<V> values() {             throw new UnsupportedOperationException("invoke spring cache abstract values method not supported");         }     } }

Shiro配置:

package me.dengfengdecao.demo.config.shiro;  import me.dengfengdecao.demo.component.cache.SpringCacheManagerWrapper; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.CachingSecurityManager; 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.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  import javax.annotation.Resource; import java.util.LinkedHashMap; import java.util.Map;  @Configuration public class ShiroConfiguration {      /*     缓存管理器 使用Redis实现      */     @Resource     private SpringCacheManagerWrapper springCacheManagerWrapper;      @Bean(name="shiroFilter")     public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();         // 必须设置 SecurityManager         shiroFilterFactoryBean.setSecurityManager(securityManager);          System.out.println("ShiroConfiguration.shiroFilterFactoryBean : shiro实例化");          // 拦截器         Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();          // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了         filterChainDefinitionMap.put("/logout", "logout");          // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问         filterChainDefinitionMap.put("/**", "authc");         filterChainDefinitionMap.put("admin/**", "authc, roles[admin]");         filterChainDefinitionMap.put("docs/**", "authc, perms[document:read]");          // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面         shiroFilterFactoryBean.setLoginUrl("/login");         // 登录成功后要跳转的链接         shiroFilterFactoryBean.setSuccessUrl("/index");         // 未授权界面;         shiroFilterFactoryBean.setUnauthorizedUrl("/403");          shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);          return shiroFilterFactoryBean;     }      /**      * shiro核心安全管理类      * @return      */     @Bean     public SecurityManager securityManager() {         CachingSecurityManager securityManager = new DefaultWebSecurityManager(myShiroRealm());         System.out.println("ShiroConfiguration.securityManager : 实例化");          // 将myShiroRealm注入到securityManager中         // securityManager.setRealm(myShiroRealm());         // 将缓存对象注入到SecurityManager中         // securityManager.setCacheManager(shiroRedisCacheManager);         securityManager.setCacheManager(springCacheManagerWrapper);         return securityManager;     }      @Bean     public MyShiroRealm myShiroRealm() {         MyShiroRealm myShiroRealm = new MyShiroRealm();         // 注入凭证匹配器         myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());          System.out.println("ShiroConfiguration.myShiroRealm : 实例化");         return myShiroRealm;     }      /**      * 凭证匹配器      * @return      */     @Bean     public HashedCredentialsMatcher hashedCredentialsMatcher() {         HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();          System.out.println("ShiroConfiguration.hashedCredentialsMatcher : 实例化");         // 加密算法         hashedCredentialsMatcher.setHashAlgorithmName("md5");         // 散列的次数,比如散列两次,相当于 md5(md5(""));         hashedCredentialsMatcher.setHashIterations(2);          return hashedCredentialsMatcher;     }      /**      * 开启shiro aop注解支持.使用代理方式;所以需要开启代码支持;<br/>      * 拦截@ RequiresPermissions注释的方法      * @param securityManager      * @return      */     @Bean     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {         AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();          System.out.println("ShiroConfiguration.authorizaionAttributeSourceAdvisor : 实例化");          authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);          return authorizationAttributeSourceAdvisor;     } } 

Realm

 import me.dengfengdecao.demo.domain.Permission; import me.dengfengdecao.demo.domain.Role; import me.dengfengdecao.demo.domain.User; import me.dengfengdecao.demo.service.UserService; import org.apache.shiro.authc.*; 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.apache.shiro.util.ByteSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory;  import javax.annotation.Resource; import java.util.Iterator; import java.util.Set;  /**  * 身份校验核心类<br/>  *  * Created by dengfengdecao on 16/10/1.  */ public class MyShiroRealm extends AuthorizingRealm {      public static final Logger log = LoggerFactory.getLogger(MyShiroRealm.class);      @Resource     private UserService userService;      @Override     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {         log.info("######## 执行Shiro权限认证 ########");          String username = (String) principalCollection.getPrimaryPrincipal();          log.info("doGetAuthorizationInfo: username <== " + username);          // 获取当前用户信息         User user = userService.findByUsername(username);          log.debug("doGetAuthorizationInfo: user <== " + user);         if (user != null) {             // 权限信息对象authorizationInfo,用来存放查出的用户的所有的角色(role)及权限(permission)             SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();              // 当前用户的角色集合             Iterator<Role> iterator = user.getRoles().iterator();             while (iterator.hasNext()) {                 authorizationInfo.addRole(iterator.next().getRole());             }              // 当前用户的角色对应的所有权限             Set<Role> roles = user.getRoles();             for (Role role : roles) {                 Iterator<Permission> iterator1 = role.getPermissions().iterator();                 while (iterator1.hasNext())                     authorizationInfo.addStringPermission(iterator1.next().getPermission());             }              return authorizationInfo;         }          // 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedUrl指定的地址         return null;     }      /**      * 用来验证用户身份      * @param authenticationToken      * @return      * @throws AuthenticationException      */     @Override     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {         log.info("######## 执行Shiro身份认证 ########");          // 获取用户登录的用户名         String username = (String) authenticationToken.getPrincipal();          log.debug("doGetAuthenticationInfo: 当前用户username <== " + username);          // 实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法         User user = userService.findByUsername(username);         log.debug("doGetAuthenticationInfo: 从数据库查出的用户user <== " + user);         if (user == null) {             return null;         } else {             //UsernamePasswordToken对象用来存放提交的登录信息             UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;              log.info("doGetAuthenticationInfo: 验证当前Subject时获取到token为:" + token);              // 加密方式             // 交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配             SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(),                 user.getPassword(), ByteSource.Util.bytes(user.getCredentialsSalt()), getName());              return authenticationInfo;         }     } } 

UserServiceImpl

@Service @Transactional @CacheConfig(cacheNames = {"user"}) public class UserServiceImpl implements UserService {      public static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);      @Resource     private UserDao userDao;      @Cacheable     @Override     public User findUserById(Long id) {         return userDao.findOne(id);     }      @Cacheable     @Override     public User findByUsername(String username) {         return userDao.findByUsername(username);     }      @CacheEvict     @Override     public void delFromCache(Long id) {         log.info("delFromCache: 移除缓存");     }      @Override     public void save(User user) {         userDao.save(user);     }      @Cacheable     @Override     public List<User> getUsers() {         return (List<User>) userDao.findAll();     }

UserController

@Controller public class UserController {      public static final Logger log = LoggerFactory.getLogger(UserController.class);      @Resource     private UserService userService;      @RequestMapping(value = "user/{userId}", method = RequestMethod.GET)     @ResponseBody     public Object findUserById(Model model, @PathVariable("userId")Long userId) {         log.info("findUserById: userId : " + userId);          User user = userService.findUserById(userId);          return user.toString();     }      @RequestMapping(value = "user", method = RequestMethod.GET)     @ResponseBody     public String findUserByUsername(@RequestParam("username")String username){         log.info("findUserByUsername: 执行");         User user = userService.findByUsername(username);          return user.toString();     }      @RequestMapping(value = "user2", method = RequestMethod.GET)     @ResponseBody     public String findUserByUsername2(@RequestParam("username")String username){         log.info("findUserByUsername2: 执行");         User user = userService.findByUsername(username);          return user.toString();     }       @RequestMapping(value = "user/delete")     @ResponseBody     public String delete(Long id) {         log.info("delete: 执行");         userService.delFromCache(id);          return "success";     }         /**      * 用户查询<br/>      * @ RequiresPermissions 进行权限管理      * @return      */     @RequestMapping("user-list")     @RequiresPermissions("user:list")     public String userList(ModelMap map){         List<User> users = userService.getUsers();         log.info("userList: users <== " + users);         map.put("users", users);          return "user/user-list";     }       /**      * 用户添加      * @return     */     @RequestMapping("user-add")     @RequiresPermissions("user:add")     public String userAdd(){         log.info("userAdd: ");         return "user/user-add";     }      /**      * 用户删除      * @return      */     @RequestMapping("user-del")     @RequiresPermissions("user:del")     public String userDelete(){         log.info("userDelete: ");         return "user/user-del";     }  }

application.properties

######################################################## ### HTTP encoding (HttpEncodingProperties) ######################################################## # Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly. spring.http.encoding.charset=UTF-8 # Enable http encoding support. spring.http.encoding.enabled=true # Force the encoding to the configured charset on HTTP requests and responses. spring.http.encoding.force=true # Force the encoding to the configured charset on HTTP requests. Defaults to true when "force" has not been specified. spring.http.encoding.force-request=true # Force the encoding to the configured charset on HTTP responses. spring.http.encoding.force-response=true   ######################################################## ### spring ######################################################## spring.mvc.view.prefix=/WEB-INF/content/ spring.mvc.view.suffix=.jsp spring.mvc.date-format=yyyy-MM-dd HH:mm:ss  spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8  ######################################################## ### REDIS (RedisProperties) ######################################################## spring.redis.database=0 spring.redis.host=127.0.0.1 spring.redis.pool.max-active=8 spring.redis.pool.max-idle=8 spring.redis.pool.max-wait=-1 spring.redis.pool.min-idle=0 spring.redis.port=6379  ######################################################## ### Java Persistence Api ######################################################## # Specify the DBMS spring.jpa.database=MYSQL # Show or not log for each sql query spring.jpa.show-sql=true # Hibernate ddl auto (create, create-drop, update) spring.jpa.hibernate.ddl-auto=update # Naming strategy # spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.ImprovedNamingStrategy # stripped before adding them to the entity manager spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect spring.jpa.properties.hibernate.format_sql=true # Register OpenEntityManagerInViewInterceptor. # Binds a JPA EntityManager to the thread for the entire processing of the request. spring.jpa.open-in-view=true

请大家帮帮分析原因。

友情提示: 问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
0条回答

一周热门 更多>