一:重复登录问题

随着互联网公司的项目在微服务和分布式的环境下进行的搭建,
导致一个项目可能分别部署在几个甚至很多的服务器集群下,此时就会出现一个问题
当用户进行一个session会话的时候,比如一个用户去登录项目,
一般的大公司的项目都是有Nginx进行反向代理的,
但是这里简单列举一下Nginx常用的几种反向代理策略:1.轮询策略,2. 权重比例策略,3. ip_hash策略,
4. 还可以自定义的策略,在Nginx的反向代理下,
一般会把用户的请求分发到不同的服务器上,
但是如果用户请求的请求是存放在该请求的服务器A上,
那么该用户的sessionID就存储在该服务器上JVM的一个ConcurrentHashmap中,以sessionID为key。
但是如果此时用户请求的一个服务模块可能需要调用到服务器B,
当用户发起请求的时候,此时的服务器B上并没有存储该用户的sessionID,
所以就会再次让用户进行一个登陆操作。还有可能会导致用户本来就想完成一个下单操作,
但是却还登陆了好几次的情况。
所以session共享方案在分布式环境和微服务系统下,显得尤其重要。

方案一:基于Nginx(或其他负载的软件或硬件)的ip_hash 负载均衡

其实就是对请求过来的ip地址对你的多少台可用的服务器进行取模,然后就会把你的请求通过Nginx的反向代理给分发到对应的服务器上。(这里会把可用的服务器放到一个数组中,如果取模得到的结果是几,就把请求分到服务器数组中的下标为几 的服务器上)

需要你在Nginx.conf文件中进行对应的修改,根据自己的可用服务器
upstream backend{
    ip_hash;
    server 192.168.128.1:8080 ;
    server 192.168.128.2:8080 ;
    server 192.168.128.3:8080 down;
    server 192.168.128.4:8080 down;
 
}
server {
    listen 8081;
    server_name test.csdn.net;
    root /home/system/test.csdn.net/test;
    location ^~ /Upload/upload {
    proxy_pass http://backend;
 
    }

在这里插入图片描述

在这里插入图片描述
方案二:基于Tomcat的session复制
这个解决方案其实就是当用户请求的时候,把产生的sessionID给复制到系统所有的服务器中,这样就能保证当用户请求的时候从服务器A可能调用到服务器B上的模块的时候,也能保证服务B也有该用户的sessionID,这样就不会再次让用户进行再次登录操作了。也就解决问题了。

1、tomcat与redis集成实现session共享:
环境为tomcat7 + jdk1.7或1.8的话:
在所有需要共享session的服务器的tomcat中目录下:
lib目录中添加以下三个jar包,测试通过:
在这里插入图片描述

conf目录中content.xml中加入:配置redis服务
在这里插入图片描述

2、如jkd1.8+tomcat7,在137和139两台tomcat中加入jar包且进行如上配置:
在这里插入图片描述
修改content.xml

在这里插入图片描述
在这里插入图片描述

添加两个注意点
1、按照如上配置,使用redis数据库,放入session中的对象必须要实现java.io.Serializable接口,使用memcache的可以不用实现Serializable接口
原因是:因为tomcat里使用的将session放置redis使用的工具类,是使用的jdk序列化模式存储的,这一点也是很容易理解的,session.setAttribute(String key, Object value),存储Object类型
object放入redis中又要能取出来,只能是序列化进行存储了,然后取出的时候进行反序列化。
所以我们在session中存储的任何对象,都必须实现序列化接口。
2、按照如上配置,使用redis做session存储空间时,web应用的session-time的时间单位会变成[秒],而不是原本的[分]
原因是:因为tomcat里使用的将session放置redis使用的工具类,在存储时为对tomcat容器时间做转换
在redis中设置过期时间是使用秒作为单位的,有个命令叫expire可以设置redis键值过期时间,所以在context.xml配置文件中我们需要制定session过期时间(默认是60秒,配成1800即30分钟),这一点很重要。
请注意!!!!
context.xml配置说明:
在这里插入图片描述
在这里插入图片描述

方案三:使用Redis做缓存session的统一缓存
每次用户的请求的时候生成的sessionID给放到Redis的服务器上。然后在基于Redis的特性进行设置一个失效时间的机制,这样就能保证用户在我们设置的Redis中的session失效时间内,都不需要进行再次登录。
一:
1、引入相关jar包:
这里需要引入Spring Session和Spring Data Redis相关的依赖jar包。可以自行从maven仓库下载,也可以参考使用的jar包:down.51cto.com/data/228183…
2、 修改spring-data-redis相关配置:
在项目的spring-data-redis相关配置中添加相关配置
3、修改web.xml:
在web.xml中添加以下filter:springSessionRepositoryFilter
org.springframework.web.filter.DelegatingFilterProxy
4、这个filter放在第一位,其他的如编码、shiro等filter需要放在这之后
到此,Spring Session和Spring Data Redis就整合到项目中了
二: Spring Session 集群解决方案
· HttpSession - 允许以应用程序容器(即 Tomcat)中性的方式替换 HttpSession。
· 将 session 所保存的状态卸载到特定的外部 session 存储中,如 Redis 或 Apache Geode
中,它们能够以独立于应用服务器的方式提供高质量的集群。
· 支持每个浏览器上使用多个 session,从而能够很容易地构建更加丰富的终端用户体验。
· 控制 session id 如何在客户端和服务器之间进行交换,这样的话就能很容易地编写 Restful API,因为它 · 可以从 HTTP头信息中获取 session id,而不必再依赖于 cookie。
· 当用户使用 WebSocket 发送请求的时候,能够保持 HttpSession 处于活跃状态。
用:spring-session-data-redis
1、引入包

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

2、Session 配置

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class SessionConfig {
}
maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,
原 Boot 的 server.session.timeout 属性不再生效。

3、实现模拟登陆
添加登陆方法和注册方法:
添加登录方法,登录成功后将用户信息存放到 Session 中。

@RequestMapping(value = "/login")
public String login (HttpServletRequest request,String userName,String password){
    String msg="登陆失败!";
    User user= userRepository.findByUserName(userName);
    if (user!=null && user.getPassword().equals(password)){
        request.getSession().setAttribute("user",user);
        msg="登陆成功!";
    }
    return msg;
}

定义 index 方法,只有用户登录之后才会看到:index content 这条信息否则提示请先登录。

@RequestMapping(value = "/index")
public String index (HttpServletRequest request){
    String msg="首页内容";
    Object user= request.getSession().getAttribute("user");
    if (user==null){
        msg="请先登录!";
    }
    return msg;
}

测试流程:
访问:http://localhost:8800/index和http://localhost:8899/index都会提示先登录。
用8800窗口登陆:localhost:8800/login?userName=xiaoli&password=123456。
登陆成功后访问首页:显示首页内容,8899端口直接访问首页,我们发现显示首页内容,则实现了session共享。

@RequestMapping(value = "/logout")
    public String logout (HttpServletRequest request){
        HttpSession session=request.getSession();
        session.removeAttribute("user");
        return "";
    }

在这里插入图片描述

方案四: 把session放到cookie中
每次用户请求的时候,都会把自己的cookie放到请求中,所以这样就能保证每次用户请求的时候都能保证用户在分布式环境下,也不会在进行二次登陆。

关于单点登录和SSO的思考

1、单点登录和传统的区别,单点登录和免登陆的区别,单点登录和内嵌方式,及iframe等方式的使用区别,

2、sso登录以后,可以将Cookie的域设置为顶域,即.a.com,这样所有子域的系统都可以访问到顶域的Cookie。我们在设置Cookie时,只能设置顶域和自己的域,不能设置其他的域。比如:我们不能在自己的系统中给baidu.com的域设置Cookie。
----如果不在一个顶级域中???跨域还跨区,Nginx可以帮助拿到cookie中的信息,就是在A系统转发中把信息写到(B服务器端)的Nginx中,然后B服务从Nginx中获取cookie的信息做验证。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐