目录
  • 单点登录 SSO(Single Sign On)
    • 什么是单点登录?
    • 实现方式
  • 开发技术
    • 单点登录实现流程
      • 实现案例
        • 看效果

          前言:

          由于考虑到cookie的安全性问题,就有了下面这个版本的sso

          单点登录 SSO(Single Sign On)

          什么是单点登录?

          单点登录的英文名叫做:Single Sign On(简称SSO),指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的系统。简而言之,多个系统,统一登陆。

          我们可以这样理解,在一个服务模块登录后,其他模块无需再登录

          实现方式

          • session广播机制实现(老方法) ​ 当模块较多时,比较浪费资源;数据冗余,存在多份一样的数据​ session默认过期时间30分钟
          • 基于cookie+redis实现​ 在项目中任何一个模块登录后,把数据放到两个地方​ redis:key:生成唯一随机值(ip、用户id等) value:用户数据​ cookie:存放redis生成的key值放到cookie​ 访问其他模块,发送请求带着cookie进行发送,服务器获取cookie值,在redis中查询,根据key进行查询,如果找到就是登录状态
          • 分布式session方式实现单点登录流程运行:(1) 用户第一次登录时,将会话信息(用户Id和用户信息),比如以用户Id为Key,写入分布式Session;(2) 用户再次登录时,获取分布式Session,是否有会话信息,如果没有则调到登录页;(3) 一般采用Cache中间件实现,建议使用Redis,因此它有持久化功能,方便分布式Session宕机后,可以从持久化存储中加载会话信息;(4) 存入会话时,可以设置会话保持的时间,比如15分钟,超过后自动超时;结合Cache中间件,实现的分布式Session,可以很好的模拟Session会话。
          • token验证在项目某个模块进行登录,登录之后,按照jwt规则生成字待串,把登录之后用户包含到生成字符串里面,把字符串返回
            (1)可以把字符串通过cookie返回
            (2)把字符串通过地址栏返回前端收到token之后将token存储在自己的请求头之中或者url后面,这样每次请求都可以带着token请求。再去访问项目其他模块,获取地址栏或者请求头里面的token,根据字符串获取用户信息。同时为了设置失效时间,可以将token放在redis中,设置失效时间,判断过期。
          • CAS 中央认证服务

          开发技术

          • SpringBoot
          • Redis
          • Session

          单点登录实现流程

          • 用户在登录时,登录成功以后得到当前sessionid
          • 将用户信息存储在redis里面,设置有效时间30分钟,以key-value形式,sessionid作为key,登录成功后的用户信息作为value
          • 访问时通过拦截器拦截请求,判断当前sessionid是否在redis里,再则延长寿命,不再提示身份过期
          • 完成登录验证后,放行执行访问请求

          实现案例

          实现效果:使用nginx做轮询分发请求,在任何一个服务登录成功以后,在访问其他服务时就不需要再去登录

          • 1,首先创建一个boot项目
          • 2,导入pom依赖
                 <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-data-redis</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.session</groupId>
                      <artifactId>spring-session-data-redis</artifactId>
                  </dependency>

          3,配置核心文件

          注意:这里只有一个配置文件,若想启动两个端口。就在编辑页面的  VM OPTIONS 里配置如下 -Dserver.port=8082

          server:
            port: 8081
          ## redis
          #session存储类型
          spring:
            application:
              name: redis_cookie
            redis:
              host: 127.0.0.1
              port: 6379
              #没用就填空
              password:
              jedis:
                pool:
                  #连接池最大连接数
                  max-active: 8
                  #阻塞时间 (负表示没有)
                  max-wait: -1
                  #最大空闲连接
                  max-idle: 8
                  #最小空闲连接
                  min-idle: 0
              #连接超时时间
              timeout: 30000
              database: 0

          4,编写用户类

          package com.gxhh.redis_session.bean;
          
          /**
           * @Program: LoginDemo
           * @ClassName User
           * @Description: 用户类
           * @Author: liutao
           * @Create: 2022/7/8 16:04
           * @Version: 1.0
           */
          public class User {
              private String username;
              private String pwd;
          
              public User() {
          
              }
              public User(String username, String pwd) {
                  this.username = username;
                  this.pwd = pwd;
              }
          
              public String getUsername() {
                  return username;
              }
          
              public void setUsername(String username) {
                  this.username = username;
              }
          
              public String getPwd() {
                  return pwd;
              }
          
              public void setPwd(String pwd) {
                  this.pwd = pwd;
              }
          
              @Override
              public String toString() {
                  return "User{" +
                          "username='" + username + ''' +
                          ", pwd='" + pwd + ''' +
                          '}';
              }
          }

          5,编写登录接口和业务逻辑

          package com.gxhh.redis_session.web;
          
          import com.fasterxml.jackson.core.JsonProcessingException;
          import com.fasterxml.jackson.databind.ObjectMapper;
          import com.gxhh.redis_session.bean.User;
          import com.gxhh.redis_session.utils.CookieUtil;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.beans.factory.annotation.Value;
          import org.springframework.data.redis.core.RedisTemplate;
          import org.springframework.data.redis.core.ValueOperations;
          import org.springframework.web.bind.annotation.GetMapping;
          import org.springframework.web.bind.annotation.PostMapping;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.RestController;
          
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.util.UUID;
          import java.util.concurrent.TimeUnit;
          /**
           * @Program: redis_cookie
           * @ClassName LoginController
           * @Author: liutao
           * @Description: 用户登录和测试接口
           * @Create: 2022-07-09 19:50
           * @Version 1.0
           **/
          @RestController
          public class LoginController {
              @Autowired
              RedisTemplate redisTemplate;
              @Autowired
              CookieUtil CookieUtil;
          
              @Value("${server.port}")
              String port;
              /**
               * 登录接口
               * @param user User对象
               * @return 提示信息
               * @throws JsonProcessingException
               */
              @PostMapping(value = "/doLogin", produces = "text/html;charset=utf-8")
              public String login(HttpServletRequest request, HttpServletResponse response, User user) throws JsonProcessingException {
                  System.out.println(user);
                  ValueOperations ops = redisTemplate.opsForValue();
                  String s = request.getSession().getId();
                      if (redisTemplate.hasKey(s)) {//登录过
                              return "重复登录";
                      } else {//未登录
                          if ("sso".equals(user.getUsername()) && "123456".equals(user.getPwd())) {
                              ObjectMapper om = new ObjectMapper();
                              ops.set(s, om.writeValueAsString(user));//将凭证存入Redis
                              redisTemplate.expire(s, 30, TimeUnit.MINUTES);//设置过期时间,30分钟
                              return "登录成功";
                          }else {
                              return "登录失败!";
                          }
                      }
              }
          
              /**
               * 退出接口
               * @return
               * @throws JsonProcessingException
               */
              @RequestMapping (value = "/logout", produces = "text/html;charset=utf-8")
              public String logout(HttpServletRequest request, HttpServletResponse response, User user) throws JsonProcessingException {
                      System.out.println(user);
                      if(redisTemplate.delete(request.getSession().getId())){
                          request.getSession().invalidate();
                          return "成功退出,请登录!";
                      }else {
                          return "系统异常!";
                      }
              }
              /**
               * 测试接口
               * @param
               * @return
               */
              @GetMapping("/hello")
              public String hello(){
                  return "hello 我是端口"+port;
              }
          }

          6,配置WebMVC拦截器,拦截所有请求,只放行登录接口

          package com.gxhh.redis_session.config;
          import com.gxhh.redis_session.interceptor.LoginInterceptor;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
          import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
          
          /**
           * @Program: redis_cookie
           * @ClassName WebMVCConfig
           * @Author: liutao
           * @Description: WebMVC拦截器
           * @Create: 2022-07-09 19:50
           * @Version 1.0
           **/
          @Configuration
          public class WebMVCConfig  implements WebMvcConfigurer  {
              @Autowired
              LoginInterceptor loginInterceptor;
              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  registry.addInterceptor(loginInterceptor)
                          .addPathPatterns("/**")//需要拦截的路径
                          .excludePathPatterns("/doLogin","/login.html") ;//排除/doLogin路径
          
              }
          
          
          }

          7,配置请求拦截器

          package com.gxhh.redis_session.interceptor;
          import com.fasterxml.jackson.databind.ObjectMapper;
          import com.gxhh.redis_session.utils.CookieUtil;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.data.redis.core.RedisTemplate;
          import org.springframework.data.redis.core.ValueOperations;
          import org.springframework.stereotype.Component;
          import org.springframework.web.servlet.HandlerInterceptor;
          
          import javax.servlet.http.Cookie;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.PrintWriter;
          import java.util.concurrent.TimeUnit;
          
          /**
           * @Program: redis_cookie
           * @ClassName LoginInterceptor
           * @Author: liutao
           * @Description: 用户登录拦截器,校验session,身份验证
           * @Create: 2022-07-09 19:50
           * @Version 1.0
           **/
          @Component
          public class LoginInterceptor implements HandlerInterceptor {
              @Autowired
              RedisTemplate redisTemplate;
              @Autowired
              CookieUtil CookieUtil;
              @Override
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                  System.out.println("拦截到请求:"+request.getRequestURI());
                  System.out.println("当前令牌:"+request.getSession().getId());
                  String s = request.getSession().getId();
                  System.out.println("登录状态:"+redisTemplate.hasKey(s));
                  if (redisTemplate.hasKey(s)) {//延长登录状态
                      redisTemplate.expire(s, 30, TimeUnit.MINUTES);//设置过期时间,30分钟
                      return true;
                  }else {//身份过期
                      response.setContentType("text/html;charset=utf-8");
                      PrintWriter out = response.getWriter();
                      out.write("身份过期,非法请求");
                      return false;
                  }
              }
          }

          8,nginx分发轮询配置

          #user  nobody;
          worker_processes  1;
          
          #error_log  logs/error.log;
          #error_log  logs/error.log  notice;
          #error_log  logs/error.log  info;
          
          #pid        logs/nginx.pid;
          events {
              worker_connections  1024;
          }
          http {
              include       mime.types;
              default_type  application/octet-stream;
          
              #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
              #                  '$status $body_bytes_sent "$http_referer" '
              #                  '"$http_user_agent" "$http_x_forwarded_for"';
              #access_log  logs/access.log  main;
              sendfile        on;
              #tcp_nopush     on;
          
              #keepalive_timeout  0;
              keepalive_timeout  65;
              #gzip  on;
              upstream mysvr{
                  server localhost:8081;
                  server localhost:8082;
              }
              server {
                  listen       8052;
                  server_name  localhost;
          
                  #charset koi8-r;
          
                  #access_log  logs/host.access.log  main;
                  #  location / {
                  #     root   html;
                  #     index  index.html index.htm;
                  #     proxy_pass http://localhost:8011;
                  # }
                  location / {
                      # root   html;
                      # index  index.html index.htm;
                      proxy_pass http://mysvr;
                  }
                  #error_page  404              /404.html;
          
                  # redirect server error pages to the static page /50x.html
                  #
                  error_page   500 502 503 504  /50x.html;
                  location = /50x.html {
                      root   html;
                  }
          
                  # proxy the PHP scripts to Apache listening on 127.0.0.1:80
                  #
                  #location ~ .php$ {
                  #    proxy_pass   http://127.0.0.1;
                  #}
                  # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
                  #
                  #location ~ .php$ {
                  #    root           html;
                  #    fastcgi_pass   127.0.0.1:9000;
                  #    fastcgi_index  index.php;
                  #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
                  #    include        fastcgi_params;
                  #}
                  # deny access to .htaccess files, if Apache's document root
                  # concurs with nginx's one
                  #
                  #location ~ /.ht {
                  #    deny  all;
                  #}
              }

          看效果

          先访问测试接口:

          SpringBoot集成redis与session实现分布式单点登录

          然后再登录:

          SpringBoot集成redis与session实现分布式单点登录

          访问测试接口:

          SpringBoot集成redis与session实现分布式单点登录

          SpringBoot集成redis与session实现分布式单点登录

          关闭浏览器后访问:

          SpringBoot集成redis与session实现分布式单点登录

          声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。