微服务gateway 获取共享session

前段时间需要搭建一个微服务架构,由于之前一直是搞单体的web应用,对微服务架构这块很短板,在搭建过程中遇到一个很棘手的问题。

项目的需求是做一个电商系统,有后台管理和用户端,因为是多用站发布,所以,后台控制的话要求多站点控制,具体需求就不多说了。

后端框架:nginx,gateway,nacos,sentinel,redist,activiti

前端:vue,jquery,bootstrap

在搭建架构的时候出现了一个问题,就是会话共享,网上看资料微服务有两个方案

1.使用Tokens ,把用户信息用JWT把用户信息加密存在用户的cookie中,或者发送给前端,让前端每次发送都带它

2.使用spring session,和传痛的session相似,只是这个session是被存放在redis中,每个服务器都是从redis取session的。

优缺点我就不说了,网上大把的。

我开始我使用的是tokens的方式,这种方式也没遇到什么问题,就是不太习惯,所以我选择了第二种,第二种,配置完成后,也成功完成了会话之间的共享。

服务注册

 测试会话共享

服务器1

 服务2

服务3(gateway,因为不是web,所以我在控制台打印了)

 之前搞这个会话共享花了也有5-6个小时,因为gateway根本不能使用EnableRedisHttpSession,spring cloud gateway是基于webflux,是非阻塞的,zuul和我们的web服务都是基于servlet的,gateway中需要将EnableRedisHttpSession注解换成EnableRedisWebSession,我替换了好像也没什么用,但是我也没管它,网上有些还说如果用gateway网关是没办法用spring session,都建议用Tokens

我这边在gateway能读取session,是因为我直接去redis读取的。

gateway配置

其它应用服务配置

 

其它应该用很简单配置好了,在启动类或配置类上加上EnableRedisHttpSession注解就可以了

 gateway不用加因为你只需要在redis读取session就可以了,毕竟gateway不做主要业务处理

但是要获取session还需要创建一个配置类

package com.example.gateway.config;

import io.lettuce.core.ClientOptions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;

//@EnableRedisWebSession
//@EnableRedisHttpSession
@Configuration
@Slf4j
public class RedisSessionConfig {
    @Value("${spring.redis.database}")
    int database;
    @Value("${spring.redis.host}")
    String host;
    @Value("${spring.redis.port}")
    int port;
    @Value("${spring.redis.password}")
    String password;
    @Bean
    public LettuceConnectionFactory lettuceConnectionFactory() {
        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(host, port);
        configuration.setPassword(RedisPassword.of(password));
        configuration.setDatabase(database);
        LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder()
                .clientOptions(ClientOptions.builder().build())
                .build();
        return new LettuceConnectionFactory(configuration,clientConfiguration);
    }

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        // 配置Redis序列化器
        return new GenericJackson2JsonRedisSerializer();
    }
    //应为映入的jar包已经有这个sessionRepository实例注入了,所以这里我随便写了一个,
    @Bean(name="sessionRepository1")
    public RedisIndexedSessionRepository sessionRepository(RedisSerializer<Object> springSessionDefaultRedisSerializer) {
        // 配置RedisSessionRepository

        RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisOperations());
        //sessionRepository.setRedisKeyNamespace("constellation:session:id");
        sessionRepository.setDefaultSerializer(springSessionDefaultRedisSerializer);
        return sessionRepository;
    }
    @Bean
    public RedisOperations<Object, Object> redisOperations() {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory());
        redisTemplate.setKeySerializer(springSessionDefaultRedisKeySerializer());
        redisTemplate.setValueSerializer(springSessionDefaultRedisSerializer());
        redisTemplate.setHashKeySerializer(springSessionDefaultRedisKeySerializer());
        redisTemplate.setHashValueSerializer(springSessionDefaultRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
    @Bean
    public RedisSerializer<String> springSessionDefaultRedisKeySerializer() {
        return new StringRedisSerializer();
    }

}

gateway测试获取应用服务的session

package com.example.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.session.Session;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Base64;

@Component
public class MainGlobalFilter implements GlobalFilter, Ordered {
    @Resource(name="sessionRepository1")
    RedisIndexedSessionRepository sessionRepository;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取 Session
        String cookieValue = exchange.getRequest().getCookies().getFirst("SESSION").getValue();
        System.out.println(cookieValue);
        String sessionId = new String(Base64.getDecoder().decode(cookieValue));
        System.out.println(sessionId);
       Session byId = sessionRepository.findById(sessionId);
        System.out.println(byId);
        if(null!=byId){
            System.out.println(byId.getAttribute("test")+"");
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

这样就可以得到session了,得到session,当然就可以得到session的换成数据了。

因为这个RedisIndexedSessionRepository.RedisSession被保护起来了,所以我只能用接口类Session来接受,RedisIndexedSessionRepository.RedisSession被保护应该是不想我们随意的去操作数据吧。反正我的gateway只是做数据验证,不做业务数据持久所以无所谓。

值得注意的是,这种方式只能读取数据没办法写入数据,可以根据直接需求决定是否这么用。

如果你非要往session里面写数据的话,只能用点手段了,也不是不可以。使用放射强行写进去就可以了。

 byId.setAttribute("test1","gateway");
            try {
                Class<?> redisSessionClass = Class.forName("org.springframework.session.data.redis.RedisIndexedSessionRepository$RedisSession");
                Method method = redisSessionClass.getDeclaredMethod("save");
                method.setAccessible(true);
                Object result = method.invoke(byId);
                System.out.println(result);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值