Springboot+redis实现session共享与单点登录---随笔

导入依赖
 <!--redis-->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <!--session-->
 <dependency>
     <groupId>org.springframework.session</groupId>
     <artifactId>spring-session-data-redis</artifactId>
 </dependency>
编码实现

Redis配置

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置
 * @author savesl
 */
@Configuration
public class RedisConfig {
	@Autowired
	private RedisConnectionFactory factory;

	@Bean
	public RedisTemplate<String, Object> redisTemplate() {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashValueSerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
		redisTemplate.setConnectionFactory(factory);
		return redisTemplate;
	}

	@Bean
	public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForHash();
	}

	@Bean
	public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
		return redisTemplate.opsForValue();
	}

	@Bean
	public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForList();
	}

	@Bean
	public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForSet();
	}

	@Bean
	public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForZSet();
	}
}

RedisSession共享配置

import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

/**
 * @author savesl
 * maxInactiveIntervalInSeconds  设置session过期时间为40*60秒【默认30分钟】
 */
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 2400)
public class RedisSessionConfig {

}

Redis工具类

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 * @author savesl
 */
@Component
public class RedisUtils {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * String类型
     */
    @Autowired
    private ValueOperations<String, String> valueOperations;
    /**
     * hash类型
     */
    @Autowired
    private HashOperations<String, String, Object> hashOperations;
    /**
     * list类型
     */
    @Autowired
    private ListOperations<String, Object> listOperations;
    /**
     * set类型
     */
    @Autowired
    private SetOperations<String, Object> setOperations;
    /**
     * sorted set类型
     */
    @Autowired
    private ZSetOperations<String, Object> zSetOperations;
    /**
     * 默认过期时长,单位:秒
     */
    public final static long DEFAULT_EXPIRE = 60 * 60 * 24;
    /**
     * 不设置过期时长
     */
    public final static long NOT_EXPIRE = -1;


    // region 通用

    /**
     * 正则表达式匹配查找key  数据量如果比较大不要使用,可以用scan代替
     * @param pattern 正则表达式
     * @return 存放key的list集合
     */
    public Set<String> getKeys (String pattern) {
        return redisTemplate.keys(pattern);
    }


    // endregion 通用

    // region string类型
    public void set(String key, Object value, long expire) {
        valueOperations.set(key, toJson(value));
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
    }

    public void set(String key, Object value) {
        set(key, value, DEFAULT_EXPIRE);
    }

    public <T> T get(String key, Class<T> clazz, long expire) {
        String value = valueOperations.get(key);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return value == null ? null : fromJson(value, clazz);
    }

    public <T> T get(String key, Class<T> clazz) {
        return get(key, clazz, NOT_EXPIRE);
    }

    public String get(String key, long expire) {
        String value = valueOperations.get(key);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return value;
    }

    public String get(String key) {
        return get(key, NOT_EXPIRE);
    }

    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 增加数量
     * @param i 递增数量
     * @return
     */
    public long incr(String key,int i) {
        return valueOperations.increment(key, i);
    }

    /**
     * 递增,每次+1
     * @return
     */
    public long incr(String key) {
//		System.out.println(key);
//		System.out.println(valueOperations);
        return valueOperations.increment(key, 1);
    }

    /**
     * 获取当前递增过的对应key的value值
     * @param key
     * @return
     */
    public long getIncrValue(String key) {
        return valueOperations.increment(key, 0);
    }

    // endregion string类型



    //region list类型
    /**
     * 获取list类型的数据    取全部数据start:0  end:-1
     * @param key
     * @param start 第一个是0 第二个是1 以此类推……
     * @param end 最后一个是-1 倒数第二个为-2 以此类推……
     * @return
     */
    public List<Object> lget(String key,long start,long end){
        return listOperations.range(key, start, end);
    }

    /**
     * 返回list类型数据的数据量,数据个数
     * @param key
     * @return
     */
    public int lsize(String key){
        return listOperations.size(key).intValue();
    }

    /**
     * 定长存入list中,只保存key对应的最近length条记录。
     * 先清理掉length-1之后的数据,然后在开头位置插入最新的数据。
     * @param key
     * @param data 数据
     * @param length 固定长度,超出length-2范围的数据会被清理
     * @return
     */
    public void pushFixLength(String key,Object data,int length) {
        //清理掉过剩的数据
        listOperations.trim(key, 0, length-2);
        //从开头保存最新的数据
        listOperations.leftPush(key, data);
    }


    // endregion list类型




    //region list-hash混合处理
    /**
     * 返回一个list中保存key的 hash类型数据集合 ,即hash类型数据的key保存在一个list中,通过list的key以获取多个hash中的数据
     * @param key list集合的key
     * @param start list的起始位置,第一个为0
     * @param end list的最后位置,倒数第一个为-1
     * @param pre hash 的key的前缀
     * @param suf hash 的key的后缀
     * @return hash数据的集合
     *
     */
    public List<Map<String,Object>> lhget(String key,long start,long end,String pre,String suf){
        List<Map<String,Object>> listMap = new ArrayList<>();
        List<Object> keys = listOperations.range(key, start, end);
        for(Object k : keys) {
            Map<String, Object> entrie = hashOperations.entries(pre+(String)k+suf);
            if(!entrie.isEmpty()) {
                listMap.add(entrie);
            }
        }
        return listMap;
    }

    // endregion list-hash混合处理



    // region hash类型
    /**
     * 获取指定key中的hash类型数据中的键值对
     * @param key
     */
    public Map<String,Object> hget(String key){
        return hashOperations.entries(key);
    }



    // endregion hash类型



    // region z-set类型

    /**
     * 指定的数据的分值自增(+1)
     */
    public double zIncr(String k,String v) {
        return zSetOperations.incrementScore(k, v, 1);
    }

    /**
     * 指定的数据分值增加给定的值(+n)
     */
    public double zIncr(String k,String v,int step) {
        return zSetOperations.incrementScore(k, v, step);
    }

    /**
     * 获取key为k的有序的分数从min到max的数据
     * @param k 键
     * @param min 最小值
     * @param max 最大值
     */
    public Set<String> zRangByScore(String k,double min,double max){
        Set<Object> setObj = zSetOperations.rangeByScore(k,min,max);
        return (Set<String>)(Set<?>)setObj;
    }

    /**
     * 获取key为k的有序的分数从min到max数据 中的从offset开始 共count个元素
     * @param k 键
     * @param min 最小值
     * @param max 最大值
     */
    public Set<String> zRangByScore(String k,double min,double max,long offset, long count){
        Set<Object> setObj = zSetOperations.rangeByScore(k,min,max,offset,count);
        return (Set<String>)(Set<?>)setObj;
    }

    /**
     * 倒序 获取key为k的有序的分数从min到max的数据
     * @param k 键
     * @param min 最小值
     * @param max 最大值
     */
    public Set<String> zReverseRangeByScore(String k,double min,double max){
        Set<Object> setObj = zSetOperations.reverseRangeByScore(k,min,max);
        return (Set<String>)(Set<?>)setObj;
    }

    /**
     * 获取key为k的有序的分数从min到max数据 中的从offset开始 共count个元素
     * @param k 键
     * @param min 最小值
     * @param max 最大值
     */
    public Set<String> zReverseRangeByScore(String k,double min,double max,long offset, long count){
        Set<Object> setObj = zSetOperations.reverseRangeByScore(k,min,max,offset,count);
        return (Set<String>)(Set<?>)setObj;
    }

    /**
     * 获取key为k的 分数从min到max数据的总数
     * @param k 键
     * @param min 最小值
     * @param max 最大值
     */
    public long zCount(String k,double min,double max){
        return zSetOperations.count(k,min,max);
    }

    /**
     * 返回指定键和值的分值
     * @param k 键
     * @return 返回分值
     */
    public Double zScore(String k,String val){
        return zSetOperations.score(k, val);
    }



    // endregion z-set类型



    /**
     * Object转成JSON数据
     */
    public String toJson(Object object) {
        if (object instanceof Integer || object instanceof Long || object instanceof Float || object instanceof Double || object instanceof Boolean || object instanceof String) {
            return String.valueOf(object);
        }
        return JSON.toJSONString(object);
    }

    /**
     * JSON数据,转成Object
     */
    public <T> T fromJson(String json, Class<T> clazz) {
        return JSON.parseObject(json, clazz);
    }


    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }


    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }


    public ValueOperations<String, String> getValueOperations() {
        return valueOperations;
    }


    public void setValueOperations(ValueOperations<String, String> valueOperations) {
        this.valueOperations = valueOperations;
    }


    public HashOperations<String, String, Object> getHashOperations() {
        return hashOperations;
    }


    public void setHashOperations(HashOperations<String, String, Object> hashOperations) {
        this.hashOperations = hashOperations;
    }


    public ListOperations<String, Object> getListOperations() {
        return listOperations;
    }


    public void setListOperations(ListOperations<String, Object> listOperations) {
        this.listOperations = listOperations;
    }


    public SetOperations<String, Object> getSetOperations() {
        return setOperations;
    }


    public void setSetOperations(SetOperations<String, Object> setOperations) {
        this.setOperations = setOperations;
    }


    public ZSetOperations<String, Object> getzSetOperations() {
        return zSetOperations;
    }


    public void setzSetOperations(ZSetOperations<String, Object> zSetOperations) {
        this.zSetOperations = zSetOperations;
    }



}

yml配置

spring:
  redis:
    database: 0 数据库索引,从0开始
    host: 服务器IP
    port: redis端口
    password: 密码
    timeout: 20000ms
    jedis:
      pool:
        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms      # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 10      # 连接池中的最大空闲连接
        min-idle: 5       # 连接池中的最小空闲连接
  session:
    store-type: redis

单点登录实现

 /**
   * 登录处理
   * @param session
   * @return
   */
 @RequestMapping(value = "/verifyAccount", method = RequestMethod.POST)
    @ResponseBody
    public Result verifyAccount(String accountName, String password, HttpSession session){
        if(StringUtil.isBlank(accountName) || StringUtil.isBlank(password)){
            return Result.error("参数错误");
        }
        AccountEntity account = accountService.findAccountByName(accountName);
        if(null == account || account.getStatus().equals("F")){
            return Result.error("该账号不存在或已禁用");
        }

        //获取redis中该用户对应的 sessionId
        String sessionId = redisUtils.get("loginAccount:"+account.getId());
        //若sessionId不为空,即:已登录
        if(StringUtil.isNotBlank(sessionId)){
            String currentSessionId = session.getId();
            if(!currentSessionId.equals(sessionId)){
                return Result.error("该账号已在其他设备登录!");
            }
        }

        if(!account.getPassword().equals(password)){
            return Result.error("密码错误");
        }

        List<String> authorization = accountService.findAllMenuByAccount(account.getId());
        Set<String> permissionSet = new HashSet<>();
        for (String auth : authorization) {
            if (StringUtils.isBlank(auth)) {
                continue;
            }
            permissionSet.addAll(Arrays.asList(auth.trim().split(",")));
        }
        account.setPermissionSet(permissionSet);
        session.setAttribute("account", account);
		
        redisUtils.set("loginAccount:"+account.getId(), session.getId(), 60*40);
        return Result.ok("登录成功!");
    }

   /**
     * 退出登录
     * @param session
     * @return
     */
    @RequestMapping("/logOut")
    public String logOut(HttpSession session){
        AccountEntity account = (AccountEntity)session.getAttribute("account");
        //设置过期
        redisUtils.set("loginAccount:"+account.getId(), session.getId(), 0);
        session.removeAttribute("account");
        return "common/login";
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用SpringBoot框架结合MyBatis实现Session共享单点登录可以借助SpringSessionRedis实现。 首先,需要配置SpringSession以使用Redis作为存储方式。可以在SpringBoot的配置文件中添加以下配置: ``` spring.session.store-type=redis spring.session.redis.namespace=spring:session spring.redis.host=127.0.0.1 spring.redis.port=6379 ``` 这样配置后,SpringSession会自动将session信息存储到Redis中。 接着,在登录验证成功后,将用户信息存储到Redis中,并将该用户的唯一标识存储到当前Session的属性中,以便后续验证是否登录。例如: ``` @RequestMapping("/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, HttpSession session) { // 验证用户名和密码 // ... // 验证通过后,将用户信息存储到Redis中,并设置Session属性 redisTemplate.opsForHash().put("user:" + username, "username", username); session.setAttribute("username", username); return "success"; } ``` 在后续的请求中,可以通过拦截器或过滤器来验证Session是否有效。例如: ``` @Component public class SessionInterceptor implements HandlerInterceptor { @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); String username = (String) session.getAttribute("username"); if (StringUtils.isEmpty(username)) { response.sendRedirect("/login"); return false; } String storedUsername = (String) redisTemplate.opsForHash().get("user:" + username, "username"); if (!StringUtils.equals(storedUsername, username)) { response.sendRedirect("/login"); return false; } return true; } } ``` 以上代码片段展示了如何通过拦截器验证Session的有效性。首先从当前Session中获取用户名,如果为空则重定向到登录页面。然后从Redis中获取存储的用户名,如果与当前用户名不匹配,则重定向到登录页面。 这样就实现SpringBoot、MyBatis、SpringSessionRedis共同完成Session共享单点登录的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值