前后端分离项目 springboot+ant design pro of vue(4)

整合redis作为缓存和会话管理

两种方式

先说比较难的

1.自己实现

导入jar包

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

application.yml 修改

  redis:
    port: 6379
    host: localhost
    password: 123456
    database: 3

shiro有默认的的缓存实现(Ehcache和内存),也可以实现 接口替换成其他的实现

要实现的接口 Cache, CacheManger

代码

package com.han.springbootshiro.config.redis;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Set;

@Component
public class RedisCache<k,v> implements Cache<k,v> {

    private String cacheName;

    @Autowired
    private RedisTemplate redisTemplate;

    public void setCacheName(String cacheName) {
        this.cacheName = cacheName;
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    }

    public RedisCache(){
    }

    @Override
    public v get(k k) throws CacheException {
        System.out.println("put key:" + k);
        //return (v) getRedisTemplate().opsForValue().get(k.toString());
        return (v) redisTemplate.opsForHash().get(this.cacheName, k.toString());
    }

    @Override
    public v put(k k, v v) throws CacheException {
        System.out.println("put key:" + k);
        System.out.println("put value:" + v);
        //getRedisTemplate().opsForValue().set(k.toString(),v);
        redisTemplate.opsForHash().put(this.cacheName, k.toString(), v);
        return null;
    }

    @Override
    public v remove(k k) throws CacheException {
        return (v)redisTemplate.opsForHash().delete(this.cacheName, k.toString());
    }

    @Override
    public void clear() throws CacheException {
        redisTemplate.delete(this.cacheName);
    }

    @Override
    public int size() {
        return redisTemplate.opsForHash().size(this.cacheName).intValue();
    }

    @Override
    public Set<k> keys() {
        return redisTemplate.opsForHash().keys(this.cacheName);
    }

    @Override
    public Collection<v> values() {
        return redisTemplate.opsForHash().values(this.cacheName);
    }
}
package com.han.springbootshiro.config.redis;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Component
public class RedisCacheManger implements CacheManager {
    @Autowired
    private RedisCache redisCache;

    @Override
    public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
        redisCache.setCacheName(cacheName);
        return redisCache;
    }
}

修改ShiroConfig.java

package com.han.springbootshiro.config.shiro;

import com.han.springbootshiro.config.redis.RedisCacheManger;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {

    @Autowired
    private RedisCacheManger redisCacheManger;

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(getSessionsSecurityManager());

        return shiroFilterFactoryBean;
    }
    @Bean
    public SessionsSecurityManager getSessionsSecurityManager(){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(getRealm());
        return defaultWebSecurityManager;
    }

    @Bean
    public Realm getRealm(){
        ShiroRealm shiroRealm = new ShiroRealm();

        //开启redis缓存管理
        shiroRealm.setCacheManager(redisCacheManger);
        shiroRealm.setCachingEnabled(true);  //开启全局缓存
        shiroRealm.setAuthenticationCachingEnabled(true);  //开启认证缓存
        shiroRealm.setAuthenticationCacheName("authenticationCache");
        shiroRealm.setAuthorizationCachingEnabled(true);  //开启授权缓存
        shiroRealm.setAuthorizationCacheName("authorizationCache");

        return shiroRealm;
    }
    
    
}

以上是使用shiro作为登录和认证缓存,下边是使用redis作会话管理,也是自己实现接口,然后替换掉默认的实现类,就是将自定义的会话管理器注册到安全管理器(SessionsSecurityManager)

redis做会话管理

继承AbstractSessionDAO,重写session的增删改查方法

package com.han.springbootshiro.config.shiro;

import com.alibaba.fastjson2.JSON;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class SelfSessionDao extends AbstractSessionDAO {

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    private final String prefix = "shiro:session";
    // 创建session,保存到数据库
    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = generateSessionId(session);

        String s = JSON.toJSONString(session);
        System.out.println("doCreate:" + s);
        // 必须要将生成的id设置到session实力当中
        assignSessionId(session,sessionId);
        redisTemplate.boundValueOps(prefix + sessionId).set(session);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        return (Session) redisTemplate.boundValueOps(prefix + sessionId).get();
    }

    @Override
    public void update(Session session) throws UnknownSessionException {
        if (session == null || session.getId() == null) {
            throw new UnknownSessionException("session 或者 session为空");
        }
        redisTemplate.boundValueOps(prefix + session.getId()).set(session);
    }

    @Override
    public void delete(Session session) {
        redisTemplate.delete(prefix + session.getId());
    }

    @Override
    public Collection<Session> getActiveSessions() {
        Set<Object> keys = redisTemplate.keys(prefix);

        if (keys != null) {
            return keys.stream().map(key -> {
                Session s = (Session) redisTemplate.boundValueOps(key).get();
                return s;
            }).collect(Collectors.toList());

        } else {
            return null;
        }
    }
}

定义自己的sessionManger,定义从请求头中获取token

package com.han.springbootshiro.config.shiro;

import com.alibaba.druid.util.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

public class CustomSessionManager extends DefaultWebSessionManager {

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader("Access-Token");
        if (StringUtils.isEmpty(id)) {
            // 获取sessionId,id可以自定义
            return super.getSessionId(request, response);
        } else {
            //返回sessionId;固定套路
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        }
    }
}

session生成逻辑

package com.han.springbootshiro.config.shiro;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;

import java.io.Serializable;
import java.util.UUID;

public class CustomSessionIdGenerator implements SessionIdGenerator {

    @Override
    public Serializable generateId(Session session) {
        //... 生成id逻辑
        return UUID.randomUUID().toString();
    }
}
//将自定义的会话管理器注册到安全管理器中

 

/**
     * 会话管理器
     * @param
     * @return
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        CustomSessionManager sessionManager = new CustomSessionManager();
        // 自定义sessionDAO
        sessionManager.setSessionDAO(selfSessionDao);
        // 自定义id生成器
        selfSessionDao.setSessionIdGenerator(new CustomSessionIdGenerator());
        return sessionManager;
    }

 @Bean
    public SessionsSecurityManager getSessionsSecurityManager(){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(getRealm());

        //将自定义的redis缓存管理器注册到安全管理器中
        defaultWebSecurityManager.setCacheManager(redisCacheManger);
        //将自定义的会话管理器注册到安全管理器中
        defaultWebSecurityManager.setSessionManager(sessionManager());
        return defaultWebSecurityManager;
    }

一个BUG

有一次偶然发现,在退出登录的时候,在redis缓存中,session清空了,但是用户的信息还在,很不理解,后来看了一下自己实现的rediscache remove 方法,打印了一下key

key竟然是个对象结构是

User{id='1',username:'12',....}

后来想会不会是我在用户身份认证时subject的principal存储的整个对象导致的

 

 但是rediscache 的其他方法的key都是 username,只有这个remove是User对象

解决方法 ShiroRealm重写 获取CacheKey方法

@Override
    protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
        User user = (User) principals.getPrimaryPrincipal();
        return user.getUsername();
    }

    @SuppressWarnings("unchecked")
    @Override
    protected Object getAuthenticationCacheKey(PrincipalCollection principals) {
        User user = (User) principals.getPrimaryPrincipal();
        return user.getUsername();
    }

后来想了想,用户登录时login方法是从UsernamePasswordToken中获取的用户名,

而subject.logout方法,是从subject获取的主身份信息,subject.getPrincipals()

应该是这两个点不同,在深的代码没有继续往下看,有兴趣的可以看看

接下来可以重启,看一下redis中信息

2.使用shiro-redis 插件

shiro-redis gitee地址

简介

大佬就是大佬,简介的确是简介

总之,使用设个插件之后,只需要少量配置,就可以完成shiro整合redis,包括缓存用户信息缓存和会话管理

jar

 <dependency>
            <groupId>org.iherus.shiro</groupId>
            <artifactId>shiro-redis-spring-boot-web-starter</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

注意:引入这两个包之后,删除原先引入的shiro和redis包,否则会冲突

shiroConfig

package com.han.springbootshiro.config.shiro;

import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.iherus.shiro.cache.redis.RedisCacheManager;
import org.iherus.shiro.cache.redis.RedisSessionDAO;
import org.iherus.shiro.cache.redis.config.RedisStandaloneConfiguration;
import org.iherus.shiro.cache.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
public class ShiroConfig {






    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(getSessionsSecurityManager());

        return shiroFilterFactoryBean;
    }
    @Bean
    public SessionsSecurityManager getSessionsSecurityManager(){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(getRealm());


        //将自定义的会话管理器注册到安全管理器中
        defaultWebSecurityManager.setSessionManager(sessionManager());
        return defaultWebSecurityManager;
    }

    @Bean
    public Realm getRealm(){
        ShiroRealm shiroRealm = new ShiroRealm();

        //开启redis缓存管理
        shiroRealm.setCacheManager(redisCacheManager());
        shiroRealm.setCachingEnabled(true);  //开启全局缓存
        shiroRealm.setAuthenticationCachingEnabled(true);  //开启认证缓存
        shiroRealm.setAuthorizationCachingEnabled(true);  //开启授权缓存


        return shiroRealm;
    }

    /**
     * 会话管理器
     * @param
     * @return
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        CustomSessionManager sessionManager = new CustomSessionManager();
        // 自定义sessionDAO
        sessionManager.setSessionDAO(sessionDAO());

        return sessionManager;
    }

    /**
     * <!-- 缓存管理器 -->
     *     <bean id="cacheManager" class="org.iherus.shiro.cache.redis.RedisCacheManager">
     *         <property name="connectionFactory" ref="connectionFactory" />
     *         <property name="database" value="2" />
     *         <property name="expirationMillis" value="90000" />
     *         <property name="keyPrefix" value="shiro:cache:" />
     *         <property name="scanBatchSize" value="3000" />
     *         <property name="deleteBatchSize" value="5000" />
     *         <property name="fetchBatchSize" value="50" />
     *     </bean>
     * @return
     */
    @Bean
    public RedisCacheManager redisCacheManager(){
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setConnectionFactory(connectionFactory());
        redisCacheManager.setKeyPrefix("shiro-redis");
        return redisCacheManager;
    }

    /**
     * <property name="clientName" value="SRC" />
     * 	<property name="connectTimeoutMillis" value="3000" />
     * 	<property name="soTimeoutMillis" value="2000" />
     * 	<property name="poolConfig" ref="poolConfig" />
     * 	<property name="configuration" ref="standaloneConfiguration" />
     * @return
     */
    @Bean
    public JedisConnectionFactory connectionFactory(){
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setClientName("shiroClient");
        jedisConnectionFactory.setPoolConfig(poolConfig());
        return jedisConnectionFactory;
    }
    @Bean
    public RedisStandaloneConfiguration redisStandaloneConfiguration(){
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHost("localhost");
        redisStandaloneConfiguration.setPort(6379);
        redisStandaloneConfiguration.setDatabase(0);

        return redisStandaloneConfiguration;
    }

    @Bean
    public JedisPoolConfig poolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        return jedisPoolConfig;
    }


    @Bean
    public SessionDAO sessionDAO(){
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO(redisCacheManager());
        return redisSessionDAO;
    }




}

官方文档是说

默认情况下,shiro-redis-spring-boot-web-starter会自动创建 RedisCacheManager 和 RedisSessionDAO 实例并注入 WebSecurityManager 和 WebSessionManager中,如果您需要自定义 SecurityManager 和 SessionManager

这块我觉得使用自动注入或者形参注入应该是行的通的,但是没实现,所以按照

Spring集成的方式

创建了bean并注入到了组件中

到现在,shiro整合redis做缓存和会话管理,算是已经结束了

后边加上密码加密就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值