java字符串共享机制_Java架构师方案—分布式session基于redis的共享机制(附完整源码)、共享session、拦截器注册与配置...

1. 读这篇文章你可以学到的东西

认识问题

这篇文章介绍的是服务在集群部署的情况下,用户的请求会被负载均衡到不同的服务器,但是用户的登入数据、权限数据、操作数据等session数据只会存在一个服务器的内存中。若用户下一次请求被转发到其他没有session数据的服务器上,那用户就还需要再重新登入,重新在网站上进行业务操作后创建操作数据。

45baf740f5ca144bbbeb59ef5a89fef4.png

用户体验极差

如果用户session数据只在一个服务器上,那用户访问另外服务器上服务时候,就会要求用户重新登入和业务操作。多次要求用户进行重新登入和业务操作,这样的体验是个灾难。

解决方案

将用户的session状态从服务中剥离出来,放到一个独立的session服务器上,这里选择redis来建session服务器。集群服务有关状态数据的操作都要和redis服务器交互。这种分布式session方案我们成为共享session服务器方案。

81fff082272e3c1b1c10ecd400b9e614.png

2. 基于redis存储共享session方案

正如上面的架构图所示,所有的应用服务器关于session数据保存和获取操作都会与session服务器交互。用户每次请求的权限验证都与session服务器中的数据进行比对。如果用户已经登入了,那登入状态数据就会保存在session服务器,用户访问其他服务器时就不需要重新登入

本文的方案,笔者提供了一个完整的demo项目,项目地址看末尾获取方式。

SpringBoot项目引入redis依赖

org.springframework.boot

spring-boot-starter-data-redis

redis.clients

jedis

org.springframework.data

spring-data-redis

redis配置

springboot项目默认使用StringRedisTemplate,StringRedisTemplate和RedisTemplate不同,前者比较简单,只需要配置redis参数就行。

第一种

spring:

redis:

#redis数据库地址

host: localhost

port:6379password:

timeout:1000#redis数据库索引,默认0

database:1

第二种

# REDIS (RedisProperties)

# Redis数据库索引(默认为0)

spring.redis.database=0# Redis服务器地址

spring.redis.host=localhost

# Redis服务器连接端口

spring.redis.port=6379# Redis服务器连接密码(默认为空)

spring.redis.password=# 连接池最大连接数(使用负值表示没有限制)

spring.redis.jedis.pool.max-active=8# 连接池最大阻塞等待时间(使用负值表示没有限制)

spring.redis.jedis.pool.max-wait=-1# 连接池中的最大空闲连接

spring.redis.jedis.pool.max-idle=8# 连接池中的最小空闲连接

spring.redis.jedis.pool.min-idle=0# 连接超时时间(毫秒)

spring.redis.timeout=5000

使用StringRedisTemplate

直接在service实现类中注入StringRedisTemplate。

@Autowiredprivate StringRedisTemplate redisTemplate;

到这里,我们项目就能正常使用redis服务了。

redis操作实现类RedisOperator

该类简单封装了String类型的redis命令:set、get, ttl等,业务逻辑可以直接调用。

/*** 使用redisTemplate的操作实现类

*@authorYI

* @date 2018-6-12 10:54:28*/@Componentpublic classRedisOperator {

@AutowiredprivateStringRedisTemplate redisTemplate;//Key(键),简单的key-value操作

/*** 实现命令:TTL key,以秒为单位,返回给定 key的剩余生存时间(TTL, time to live)。

*

*@paramkey

*@return

*/

public longttl(String key) {returnredisTemplate.getExpire(key);

}

......//因类代码比较多,这里就不全部展示了,读者可以查阅项目demo

3. 登入数据存放到session服务器

用户登入

用户登入接口逻辑:

获取username和password数据,与数据库保存的用户密码比对。

用户名和密码比对正确后,生成唯一的uuid作为用户的登入状态session数据。

通过redis操作类将session数据保存在redis中,key由username生成,并设置30分钟有效时间。

将用户username保存到cookie中。

@PostMapping(value = "loginCheck")

@ResponseBodypublicRestResponseBo loginCheck(@RequestParam String username,

@RequestParam String password,

HttpServletRequest request,

HttpServletResponse response) {if (StringUtils.isEmpty(username) ||StringUtils.isEmpty(password)){return RestResponseBo.fail("用户名或者密码不能为空!");

}if(!username.equals("admin") || !password.equals("admin"))

{return RestResponseBo.fail("用户名或者密码不正确!");

}

String token=StrUtil.uuid();//存放唯一的 token 并设置过期时间

operator.set(JdkApiInterceptor.USER_REDIS_SESSION + ":" +username, token, REDIS_TIMEOUT);//设置用户 密码 token等信息

operator.set(username, username+":"+password+":"+token);//用户浏览器会存放两种cookie: userToken,userId。

CookieUtil.addCookie("userName", username);return RestResponseBo.ok();//}

拦截器JdkApiInterceptor校验用户登入状态

拦截逻辑:

先从请求cookie 中获取username。

再查询redis中该用户的登入session数据。

不为空则是已登入,为空则是未登入或登入状态过期。

未登入或登入状态过期则重定向到登入页面。

@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throwsException {//获取用户cookies

String userName = CookieUtil.getCookie("userName");//放开登入接口//String uri = request.getRequestURI();//logger.info("请求uri:" + uri);//

//if(uri.equals("loginCheck"))//return true;//

logger.info(" ======= 拦截UserId:" +userName);//用户id和token都不为空

if (!StringUtils.isEmpty(userName)) {//根据userid生成唯一key从redis中查出唯一token

String uniqueToken = redis.get(USER_REDIS_SESSION + ":" +userName);

logger.info("拦截uniqueToken:" +uniqueToken);//如果唯一token为空 ,则拦截url重定向到登入页面

if(StringUtils.isEmpty(uniqueToken)) {

response.sendRedirect("/login");

returnErrorResponse(response,"请登录...");return false;

}//用户id和token有一个为空,则重定向登入页面

} else{

response.sendRedirect("/login");

returnErrorResponse(response,"请登录...");return false;

}return true;

}

到此,就能实现不同服务器的session共享了。

4. 拦截器注册及配置

注册

使用@Configuration修饰创建配置类WebSecurityConfig

@Configurationpublic class WebSecurityConfig implementsWebMvcConfigurer {

...

创建拦截器bean对象,放入ioc容器。

@BeanpublicJdkApiInterceptor JdkApiInterceptor(){return newJdkApiInterceptor();

}

配置

配置拦截器要拦截的url和不拦截的url

/*** 拦截器注册 设置拦截接口

*@paramregistry*/@Overridepublic voidaddInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(JdkApiInterceptor()).addPathPatterns("/**")//设置拦截所有的路径//排除的路径:静态资源路径。防止被JdkApiInterceptor拦截

.excludePathPatterns("/loginCheck","/login","/error","/js/**", "/css/**", "/imag/**");

}

这里排除拦截的url中,除了静态资源路径和登入接口,为什么还有/error路径呢?具体原因请查看我这篇文章:WebMvcConfigurer的excludePathPatterns配置 "失效" 问题

5. 简单谈谈我对redis共享session方案的看法

基于redis的共享session方案能解决用户重复登入的问题,带来更好的用户体验,是分布式架构经典的解决方案,但同时也带了问题,redis服务器的高可用问题以及远处通信的性能问题。当然集群下session问题解决方案有很多种,这只是其中之一,日后我还会进一步以文章形式与大家探讨。

查看更多 “Java架构师方案” 系列文章 以及 SpringBoot2.0学习示例

介绍

SpringBoot2.0-Jackdking 使用的各种示例,整合流行的中间件,此开源项目中的每个示例都是站在初学者角度,细心剖析技术实现细节,帮助初学者快速掌握 Spring Boot2.0 各组件的使用。

文章

如果大家觉得这篇文章对你学习架构有帮助的话,还请点赞,在看支持一下。github项目也记得点个星哦!

完整的demo项目,请关注公众号“前沿科技bot“并发送"redis共享session"获取。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值