• Spring Boot + Redis 实现Shiro集群


      为实现Web应用的分布式集群部署,要解决登录session的统一。本文利用shiro做权限控制,redis做session存储,结合spring boot快速配置实现session共享。

      1、引入相关依赖

    复制代码
            <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
    </dependency>
    复制代码

      2、Redis相关

      2.1.redis配置

      spring.redis.host=localhost
      spring.redis.port=6379
      spring.redis.password=

      2.2.redis缓存的对象必须序列化,通用序列化 

    复制代码
    import org.springframework.core.convert.converter.Converter;
    import org.springframework.core.serializer.support.DeserializingConverter;
    import org.springframework.core.serializer.support.SerializingConverter;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.SerializationException;

    /**

    • redis序列化对象

      */

      public class RedisObjectSerializer implements RedisSerializer<Object> {

      private Converter<Object, byte[]> serializer = new SerializingConverter();

      private Converter<byte[], Object> deserializer = new DeserializingConverter();

      static final byte[] EMPTY_ARRAY = new byte[0];

      public Object deserialize(byte[] bytes) {

      if (isEmpty(bytes)) {

      return null;


      }
      try {

      return deserializer.convert(bytes);

      }
      catch (Exception ex) {

      throw new SerializationException("Cannot deserialize", ex);

      }

      }

      public byte[] serialize(Object object) {

      if (object == null) {

      return EMPTY_ARRAY;

      }

      try {

      return serializer.convert(object);

      }
      catch (Exception ex) {

      return EMPTY_ARRAY;

      }

      }

      private boolean isEmpty(byte[] data) {

      return (data == null || data.length == 0);

      }

      }

复制代码

  2.3 RedisTemplate 配置

复制代码
import org.springframework.cache.CacheManager;
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.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
* redis 配置
*/
@Configuration
public class RedisConfig {

@Bean</br>
</span><span style="color: #0000ff">public</span> CacheManager cacheManager(RedisTemplate&lt;Object, Object&gt;<span style="color: #000000"> redisTemplate) {</br>
    RedisCacheManager cacheManager </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> RedisCacheManager(redisTemplate);</br>
    cacheManager.setDefaultExpiration(</span>1800<span style="color: #000000">);</br>
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> cacheManager;</br>
}</br></br>

@Bean</br>
</span><span style="color: #0000ff">public</span> RedisTemplate&lt;Object, Object&gt;<span style="color: #000000"> redisTemplate(RedisConnectionFactory factory) {</br>
    RedisTemplate</span>&lt;Object, Object&gt; template = <span style="color: #0000ff">new</span> RedisTemplate&lt;Object, Object&gt;<span style="color: #000000">();</br>
    template.setConnectionFactory(factory);</br>
    template.setKeySerializer(</span><span style="color: #0000ff">new</span><span style="color: #000000"> StringRedisSerializer());</br>
    template.setValueSerializer(</span><span style="color: #0000ff">new</span><span style="color: #000000"> RedisObjectSerializer());</br>
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> template;</br>
}</br>

}

复制代码

   3.Redis实现shiro的SessionDao存取session

复制代码
import java.io.Serializable;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.shiro.session.Session;

import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Component;

/**

  • redis实现共享session

    */

    @Component

    public class RedisSessionDAO extends EnterpriseCacheSessionDAO {

    private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);

    // session 在redis过期时间是30分钟30*60

    private static int expireTime = 1800;

    private static String prefix = "weiyou-shiro-session:";

    @Resource

    private RedisTemplate<String, Object> redisTemplate;

    // 创建session,保存到数据库

    @Override

    protected Serializable doCreate(Session session) {

    Serializable sessionId
    = super.doCreate(session);

    logger.debug(
    "创建session:{}", session.getId());

    redisTemplate.opsForValue().set(prefix
    + sessionId.toString(), session);

    return sessionId;

    }

    // 获取session

    @Override

    protected Session doReadSession(Serializable sessionId) {

    logger.debug(
    "获取session:{}", sessionId);

    // 先从缓存中获取session,如果没有再去数据库中获取

    Session session = super.doReadSession(sessionId);

    if (session == null) {

    session
    = (Session) redisTemplate.opsForValue().get(prefix + sessionId.toString());

    }

    return session;

    }

    // 更新session的最后一次访问时间

    @Override

    protected void doUpdate(Session session) {

    super.doUpdate(session);

    logger.debug(
    "获取session:{}", session.getId());

    String key
    = prefix + session.getId().toString();

    if (!redisTemplate.hasKey(key)) {

    redisTemplate.opsForValue().set(key, session);

    }

    redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);

    }

    // 删除session

    @Override

    protected void doDelete(Session session) {

    logger.debug(
    "删除session:{}", session.getId());

    super.doDelete(session);

    redisTemplate.delete(prefix
    + session.getId().toString());

    }

    }

复制代码

  4.实现cache共享

复制代码
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.shiro.cache.Cache;

import org.apache.shiro.cache.CacheException;

import org.springframework.data.redis.core.RedisTemplate;

@SuppressWarnings("unchecked")

public class ShiroCache<K, V> implements Cache<K, V> {

</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> String REDIS_SHIRO_CACHE = "weiyou-shiro-cache:"<span style="color: #000000">;</br>
</span><span style="color: #0000ff">private</span><span style="color: #000000"> String cacheKey;</br>
</span><span style="color: #0000ff">private</span> RedisTemplate&lt;K, V&gt;<span style="color: #000000"> redisTemplate;</br>
</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">long</span> globExpire = 30<span style="color: #000000">;</br></br>

@SuppressWarnings(
"rawtypes")
public ShiroCache(String name, RedisTemplate client) {
this.cacheKey = REDIS_SHIRO_CACHE + name + ":";
this.redisTemplate = client;
}
@Override</br>
</span><span style="color: #0000ff">public</span> V get(K key) <span style="color: #0000ff">throws</span><span style="color: #000000"> CacheException {</br>
    redisTemplate.boundValueOps(getCacheKey(key)).expire(globExpire, TimeUnit.MINUTES);</br>
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> redisTemplate.boundValueOps(getCacheKey(key)).get();</br>
}</br>

@Override</br>
</span><span style="color: #0000ff">public</span> V put(K key, V value) <span style="color: #0000ff">throws</span><span style="color: #000000"> CacheException {</br>
    V old </span>=<span style="color: #000000"> get(key);</br>
    redisTemplate.boundValueOps(getCacheKey(key)).set(value);</br>
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> old;</br>
}</br>

@Override</br>
</span><span style="color: #0000ff">public</span> V remove(K key) <span style="color: #0000ff">throws</span><span style="color: #000000"> CacheException {</br>
    V old </span>=<span style="color: #000000"> get(key);</br>
    redisTemplate.delete(getCacheKey(key));</br>
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> old;</br>
}</br></br>

@Override</br>
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> clear() <span style="color: #0000ff">throws</span><span style="color: #000000"> CacheException {</br>
    redisTemplate.delete(keys());</br>
}</br>

@Override</br>
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">int</span><span style="color: #000000"> size() {</br>
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> keys().size();</br>
}</br></br>

@Override</br>
</span><span style="color: #0000ff">public</span> Set&lt;K&gt;<span style="color: #000000"> keys() {</br>
    </span><span style="color: #0000ff">return</span> redisTemplate.keys(getCacheKey("*"<span style="color: #000000">));</br>
}</br></br>

@Override</br>
</span><span style="color: #0000ff">public</span> Collection&lt;V&gt;<span style="color: #000000"> values() {</br>
    Set</span>&lt;K&gt; set =<span style="color: #000000"> keys();</br>
    List</span>&lt;V&gt; list = <span style="color: #0000ff">new</span> ArrayList&lt;&gt;<span style="color: #000000">();</br>
    </span><span style="color: #0000ff">for</span><span style="color: #000000"> (K s : set) {</br>
        list.add(get(s));</br>
    }</br>
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> list;</br>
}</br></br>

</span><span style="color: #0000ff">private</span><span style="color: #000000"> K getCacheKey(Object k) {</br>
    </span><span style="color: #0000ff">return</span> (K) (<span style="color: #0000ff">this</span>.cacheKey +<span style="color: #000000"> k);</br>
}</br>

}

复制代码

  实现shiro 的CacheManager

复制代码
import javax.annotation.Resource;

import org.apache.shiro.cache.Cache;

import org.apache.shiro.cache.CacheException;

import org.apache.shiro.cache.CacheManager;

import org.springframework.data.redis.core.RedisTemplate;

public class RedisCacheManager implements CacheManager {

@Resource</br>
</span><span style="color: #0000ff">private</span> RedisTemplate&lt;String, Object&gt;<span style="color: #000000"> redisTemplate;</br></br>

@Override</br>
</span><span style="color: #0000ff">public</span> &lt;K, V&gt; Cache&lt;K, V&gt; getCache(String name) <span style="color: #0000ff">throws</span><span style="color: #000000"> CacheException {</br>
    </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">new</span> ShiroCache&lt;K, V&gt;<span style="color: #000000">(name, redisTemplate);</br>
}</br></br>

</span><span style="color: #0000ff">public</span> RedisTemplate&lt;String, Object&gt;<span style="color: #000000"> getRedisTemplate() {</br>
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> redisTemplate;</br>
}




public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {

this.redisTemplate = redisTemplate;

}

}

复制代码

 5.配置

  

复制代码
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.shiro.session.mgt.SessionManager;

import org.apache.shiro.spring.LifecycleBeanPostProcessor;

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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

/**


*

  • @author April.Chen

    */
    //@Configuration

    public class ShiroConfig {

    @Resource

    private RedisSessionDAO sessionDAO;

    @Bean

    public UserRealm getUserRealm() {

    return new UserRealm();

    }

    @Bean

    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {

    return new LifecycleBeanPostProcessor();

    }

    @Bean

    public RedisCacheManager redisCacheManager() {

    return new RedisCacheManager();

    }

    @Bean

    public SessionManager sessionManager() {

    DefaultWebSessionManager sessionManager
    = new DefaultWebSessionManager();

    sessionManager.setSessionDAO(sessionDAO);

    sessionManager.setGlobalSessionTimeout(
    1800);

    sessionManager.setCacheManager(redisCacheManager());

    return sessionManager;

    }

    @Bean

    public DefaultWebSecurityManager securityManager() {

    DefaultWebSecurityManager securityManager
    = new DefaultWebSecurityManager();

    securityManager.setSessionManager(sessionManager());

    securityManager.setCacheManager(redisCacheManager());

    return securityManager;

    }

    @Bean

    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {

    AuthorizationAttributeSourceAdvisor aasa
    = new AuthorizationAttributeSourceAdvisor();

    aasa.setSecurityManager(securityManager());

    return new AuthorizationAttributeSourceAdvisor();

    }

    @Bean

    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {

    DefaultAdvisorAutoProxyCreator daap
    = new DefaultAdvisorAutoProxyCreator();

    daap.setProxyTargetClass(
    true);

    return daap;

    }

    @Bean

    public ShiroFilterFactoryBean getShiroFilterFactoryBean() {

    Map
    <String, String> filterChainDefinitionMap = new HashMap<>();

    ShiroFilterFactoryBean shiroFilterFactoryBean
    = new ShiroFilterFactoryBean();

    shiroFilterFactoryBean.setSecurityManager(securityManager());

    shiroFilterFactoryBean.setLoginUrl(
    "/login");

    shiroFilterFactoryBean.setSuccessUrl(
    "/index");

    filterChainDefinitionMap.put(
    "/sa/", "authc");

    filterChainDefinitionMap.put(
    "/
    ", "anon");

    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    return shiroFilterFactoryBean;

    }

    }

复制代码
  • 相关阅读:
    4年Java程序员十面阿里终拿下offer,评级P6+年薪30-40w无股票
    真香警告!手绘172张图解HTTP协议+703页TCP/IP协议笔记
    Git官方和创始人都推荐的Git权威指南,广度深度和实战性史无前例
    阿里“教授”总结整理手写大型网站技术架构:核心原理与案例分析
    GitHub上120K Stars国内第一的Java多线程PDF到底有什么魅力?
    霸榜GitHub必读书籍:编写高质量代码改善Java程序员的151个建议
    GitHub上260K Stars的P8架构师纯手写的Java高并发编程详解
    LeetCode每日一题:802 找到最终安全状态
    LeetCode每日一题:662二叉树最大宽度
    Springboot之security框架 登录安全验证授权流程
  • 原文地址:https://www.cnblogs.com/jpfss/p/8427817.html
  • Copyright © 2020-2023  润新知