十六.SpringCloud+SpringSession实现微服务单点登录

Session不共享问题

对于登录而言,通常情况下我们喜欢把登录信息存储到服务器的Session中,这种存储方式在单体应用中没有问题,但是在分布式/集群环境中会存在Session丢失问题,如下图:
在这里插入图片描述
解决方案也有很多,在《微服务认证授权方案》一文中有相关的解决方案分析,这里不在重复赘述,本文章的目的是使用SpringSession+Redis来解决分布式系统中的单点登问题

SpringSession的认证方案

这种认证方案还是使用的是session,只不过是将Session统一存储到Redis中实现session共享,各个服务可以从Redis中获取Session得到认证信息,然后做身份检查,权限校验等工作,当然整个流程我们可以自己实现,但是如何把session存储到Redis以及从Redis中取出Session是一个麻烦的事情,Spring提供了解决方案SpringSession
在这里插入图片描述
SpringSession干的工作就是修改getSession和setSession方法基于Redis存取Session,同时要注意的是Session是通过Cookie传递sessionid的,如果要让多个服务都能接受到sessionid拿到session,需要把cookie的存储域扩大,如有三个系统“auth.mall.com”,“goods.maill.com”,“stock.mall.com”,那我们需要把domian设置成“mall.com”,这样一来浏览器带着cookie请求任何一个系统都可以获取到sessionid,然后从Redis中获取Session。

ps:如果您还不知道session和cookie的执行流程,请先去查查资料

SpringSession入门

官网文档在这

这里我们使用SpringBoot应用为例,我们需要导入spring-session-data-redis依赖,Spring Boot 会自动创建了一个名为springSessionRepositoryFilter的Filter,它负责更换原生的HttpSession为自定义实现,比如基于Redis实现

1.搭建项目,集成SpringSession

这里是以SpringBoot为例,你需要搭建一个SpringBoot的应用,然后导入SpringSession整合Redis的依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>
    <dependencies>
 		<dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
	    <dependency>
	     	 <groupId>org.springframework.boot</groupId>
	         <artifactId>spring-boot-starter-web</artifactId>
	    </dependency>
    </dependencies>

data-redis默认基于lettuce实现,我这里更换成了jedis

2.配置SpringSession存储方式

然后把session配置存储类型为Redis,以及配置Redis链接信息

#server.servlet.session.timeout =#会话超时。如果未指定持续时间后缀,则使用秒。
spring:
  session:
    store-type: redis
  redis:
    host: localhost
    port: 6379
    # 选择redis的数据库的分库
    database: 0
    password: 123456
    #redis连接池配置
    jedis:
      pool:
        max-idle: 10
        min-idle: 5
        max-active: 100
        max-wait: 3000
        timeout: 6005
3.SpringSession的Java配置

最后编写SpringSesson配置类,通过@EnableRedisHttpSession 注解开启SpringSession ,通过该注解,SpringSession会创建一个Servlet过滤器,该过滤器用Spring 定义的Session替换HttpSession 实现Redis存储。

//开启SpringSession,基于Redis存储,maxInactiveIntervalInSeconds是session失效时间
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=1800)
public class SpringSessionConfig {

}

注意:该配置类需要被@SpringBootApplication扫描到

4.Session存储实体类测试

实体类,需要实现Serializable接口

public class VipUser implements Serializable  {
	 private Long id;
	 private String password;
	 private String nickName;
	 ...省略...
}

使用sesson存取一个User对象

@RestController
public class LoginController {

    @RequestMapping("/login")
    public void login(HttpSession session){
        VipUser user = new VipUser();
        user.setId(1L);
        user.setNickName("王大锤");
        user.setPassword("123456");
        String key = "user";
        //添加属性到Session
        session.setAttribute(key,user);
        //从sesson中取出属性值
        VipUser userFromSession = (VipUser)session.getAttribute(key);

        System.out.println(userFromSession.getId()+" , "+userFromSession.getNickName()+" , "+userFromSession.getPassword());
    }
}

上面的代码很简单,使用session存储一个user , 启动好Redis服务器,使用Postmain访问 /login ,观察Redis中的数据
在这里插入图片描述
Redis效果如下:

在这里插入图片描述

看到这个效果,说明session确实存储到了Redis,并且可以正常的完成存取操作。

SpringSession的自定义配置
1.定义Redis序列化方式

SpringSession把值存储到Redis中默认情况下使用的是JDK的序列化方式,它要求我们存储的实体类需要实现Serializable接口,我们希望Redis以一种更友好更通用的方式去存储数据,即:JSON,我们可以通过定义序列化器来实现JSON方式的数据存储

//开启SpringSession,基于Redis存储,maxInactiveIntervalInSeconds是session失效时间
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=1800)
public class SpringSessionConfig {

	/**
	 * 更换序列化器,使用JSON的序列化器
	 */
	//@Bean("springSessionDefaultRedisSerializer")
	public RedisSerializer setSerializer(){
		return new GenericJackson2JsonRedisSerializer();
	}
}
2.定义cookie的序列化器

在前面我们讨论过,在分布式环境中,一个系统由多个子系统组成,如果要实现单点登录就要实现cookie跨域共享,否则在不同的子系统中是获取不到Session的,我们可以通过设置cookie的domain来扩大cookie的作用域,比如:有三个子系统分别是“auth.mall.com”,“goods.mall.com”,“stock.mall.com”,那么我们把cookie的domain设置为“mall.com”,那三个子系统就都可以获取到cookie,从而三个子系统都可以获取到相同的session了。

定义CookieSerializer 序列化器,如下:

//开启SpringSession,基于Redis存储,maxInactiveIntervalInSeconds是session失效时间
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=1800)
public class SpringSessionConfig {

	/**
	 * 更换序列化器
	 */
	//@Bean("springSessionDefaultRedisSerializer")
	public RedisSerializer setSerializer(){
		return new GenericJackson2JsonRedisSerializer();
	}

	/**
	 * 设置cookie域
	 */

	@Bean
	public CookieSerializer cookieSerializer(){
		DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
		//cookieSerializer.setCookieMaxAge();
		cookieSerializer.setCookieName("mysession");	//cookie的名字
		cookieSerializer.setDomainName("mall.com");		//cookie的域
		cookieSerializer.setUseHttpOnlyCookie(false);	//只是支持http
		return cookieSerializer;
	}
}
3.测试

在这里插入图片描述

文章结束,最后附上一张相对完整的分布式应用登录的流程图,希望对你有所帮助

在这里插入图片描述

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
使用SpringBoot框架结合MyBatis实现Session共享和登录可以借助SpringSession和Redis来实现。 首先,需要配置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、SpringSession和Redis共同完成Session共享和登录的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨家巨子@俏如来

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值