• 单点登录SSO(Single Sign On)


    • 单点登录的三种常见方法
      • session广播机制(不常用)
        • 通过session登录之后,将session的值复制到每个模块中
      • cookies和redis实现
        • 在任意一个模块进行登录,登录之后将登录数据存到两个地方
          • redis:key生成唯一随机值,value是用户数据
          • cookie:把redis生成的key值存放到cookie中
        • 访问项目模块时,需要带着cookie发送请求,根据请求带来的cookie到redis查询,查询即表示登录
      • token实现(令牌机制)
        • token表示按照一定的规则(通用的、官方的,如JWT)生成的字符串(可以包含用户的信息)
          • jwt头部信息
          • 有效载荷,包含用户主体信息
          • 签名哈希,防伪标志
        • 在任意一个模块进行登录,登录之后按照一定的规则将登录之后的用户数据生成字符串,通过两种方法方式返回
          • 可以把字符串通过cookie返回
          • 把字符串加到地址栏返回
            • 访问项目模块时,地址栏需要带着生成的字符串发送请求,根据字符串获取用户信息,获取成功即表示登录
        • 导入依赖
          <!-- JWT -->
          <dependency>
              <groupId>io.jsonwebtoken</groupId>
              <artifactId>jjwt</artifactId>
              <version>0.7.0</version>
          </dependency>
          
        • JWT工具类(内容较为为固定,易知放在公共模块中)
          package com.atguigu.commonutils;
          
          import io.jsonwebtoken.Claims;
          import io.jsonwebtoken.Jws;
          import io.jsonwebtoken.Jwts;
          import io.jsonwebtoken.SignatureAlgorithm;
          import org.springframework.http.server.reactive.ServerHttpRequest;
          import org.springframework.util.StringUtils;
          
          import javax.servlet.http.HttpServletRequest;
          import java.util.Date;
          
          public class JwtUtils {
              // token过期时间
              public static final long EXPIRE = 1000 * 60 * 60 * 24;
              // 密钥(随意写,企业中有规定的字符串)
              public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
          
              // 生成token字符串的方法
              public static String getJwtToken(String id, String nickname){
          
                  String JwtToken = Jwts.builder()
                          // 设置头部参数
                          .setHeaderParam("typ", "JWT")
                          .setHeaderParam("alg", "HS256")
                          // 设置分类以及过期时间
                          .setSubject("guli-user")
                          .setIssuedAt(new Date())
                          .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                          // 设置token主体部分,存储用户信息,不限个数(属性)
                          .claim("id", id)
                          .claim("nickname", nickname)
                          // 设置签名哈希,防伪标志
                          .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                          .compact();
          
                  return JwtToken;
              }
          
              /**
               * 判断token是否存在与有效
               * @param: jwtToken
               * @return
               */
              public static boolean checkToken(String jwtToken) {
                  if(StringUtils.isEmpty(jwtToken)) return false;
                  try {
                      Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
                  } catch (Exception e) {
                      e.printStackTrace();
                      return false;
                  }
                  return true;
              }
          
              /**
               * 判断token是否存在与有效
               * @param request
               * @return
               */
              public static boolean checkToken(HttpServletRequest request) {
                  try {
                      String jwtToken = request.getHeader("token");
                      if(StringUtils.isEmpty(jwtToken)) return false;
                      Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
                  } catch (Exception e) {
                      e.printStackTrace();
                      return false;
                  }
                  return true;
              }
          
              /**
               * 根据token获取会员id
               * @param request
               * @return
               */
              public static String getMemberIdByJwtToken(HttpServletRequest request) {
                  String jwtToken = request.getHeader("token");
                  if(StringUtils.isEmpty(jwtToken)) return "";
                  Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
                  Claims claims = claimsJws.getBody();
                  // 和上面的设置主体部分属性名称一样即可,获取什么可根据需求
                  return (String)claims.get("id");
              }
          }
          
        • 注册时,填写相关信息,获取验证码调用接口的业务代码(可参考博主的“SpringBoot整合阿里云短信服务”)
        • 登录成功时需要返回按照JWT(JWT工具类)规则生成的token字符串的业务代码(其中需求可改)
          @Override
          public String userLogin(UcenterMember member) throws MyException {
              String mobile = member.getMobile();
              String password = member.getPassword();
          
              // 手机号和密码是否为空
              if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
                  throw new MyException(20001, "登录失败!");
              }
          
              // 判断手机号是否正确
              QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
              wrapper.eq("mobile", mobile);
              UcenterMember ucenterMember = this.getOne(wrapper);
              if(ucenterMember == null) {
                  throw new MyException(20001, "手机号错误!");
              }
              // 判断密码是否正确
              // 将输入密码加密与数据库校验(每个企业的加密方式不一样)
              if(!MD5.encrypt(password).equals(ucenterMember.getPassword())) {
                  throw new MyException(20001, "密码错误!");
              }
          
              // 判断用户的状态(禁用 or 活跃)
              if(ucenterMember.getIsDisabled()) {
                  throw new MyException(20001, "登录失败!");
              }
          
              // 按照JWT的规则生成token字符串
              String id = ucenterMember.getId();
              String nickname = ucenterMember.getNickname();
              String token = JwtUtils.getJwtToken(id, nickname);
              return token;
          }
          
        • 前端发送请求时将token带回来进行解析(JWT工具类中已有解析token的方法)获取其中的用户主题信息,返回用户的信息给前端,
          // 根据token获取用户信息
          @GetMapping("getMemberInfo")
          public R getMemberInfo(HttpServletRequest request) {
              // 调用jwt工具类的方法,根据request对象获取头信息,返回用户id
              String memberId = JwtUtils.getMemberIdByJwtToken(request);
              // 查询数据库根据用户id获取用户信息
              UcenterMember member = memberService.getById(memberId);
              return R.ok().data("memberInfo", member);
          
          }
          
        • 接下来单点登录的重头戏,vue先下载js-cookie(前端代码)
          • 上面登录成功时返回的token字符串,前端将token字符串放到cookie里面
            //第二步 获取token字符串放到cookie里面
            //第一个参数cookie名称,第二个参数值,第三个参数作用范围
            cookie.set('guli_token',response.data.data.token,{domain: 'localhost'})
            
          • 创建前端拦截器,判断cookie里面是否有token字符串,如果有,就将token字符串放到header(请求头)中
            import axios from 'axios';
            import cookie from 'js-cookie';
            
            // 创建axios实例
            const service = axios.create({
              baseURL: 'http://localhost:9001', // api的base_url
              timeout: 20000 // 请求超时时间
            });
            
            //第三步 创建拦截器  http request 拦截器
            service.interceptors.request.use(
              config => {
              //debugger
              //判断cookie里面是否有名称是guli_token数据
              if (cookie.get('guli_token')) {
                //把获取cookie值放到header里面
                config.headers['token'] = cookie.get('guli_token');
              }
                return config
              },
              err => {
              return Promise.reject(err);
            });
            
          • 根据token值调用相关的接口,根据token获取用户信息,将返回的用户信息放到cookie中,为了显示页面(主页)
            //第四步 调用接口 根据token获取用户信息,为了首页面显示
            loginApi.getLoginUserInfo()
                .then(response => {
                    this.loginInfo = response.data.data.memberInfo
                    //获取返回用户信息,放到cookie里面
                    cookie.set('guli_ucenter',this.loginInfo,{domain: 'localhost'})
            
                    //跳转页面
                    window.location.href = "/";
                })
            
          • 完整的代码大致如下
            //登录的方法
            submitLogin() {
                //第一步 调用接口进行登录,返回token字符串
                loginApi.submitLoginUser(this.user) 
                    .then(response => {
                        //第二步 获取token字符串放到cookie里面
                        //第一个参数cookie名称,第二个参数值,第三个参数作用范围
                        cookie.set('guli_token',response.data.data.token,{domain: 'localhost'})
                     
                        //第四步 调用接口 根据token获取用户信息,为了首页面显示
                        loginApi.getLoginUserInfo()
                            .then(response => {
                                // 一定要在这里转成字符串
                                this.loginInfo = JSON.stringify(response.data.data.memberInfo);
                                //获取返回用户信息,放到cookie里面
                                cookie.set('guli_ucenter',this.loginInfo,{domain: 'localhost'})
                
                                //跳转页面
                                window.location.href = "/";
                            })
                   })
            }
            
          • 从cookie的获取数据显示到页面中
            var user = "cookie中存放用户的信息(上面将json格式转成了字符串类型)"
            // 将信息显示到页面中
            this.userInfo = JSON.parse(user);
            
  • 相关阅读:
    抚琴弹唱东流水
    借点阳光给你
    日月成双行影单
    一夜飘雪入冬来
    悼念钱学森
    我的青春谁作主
    重游望江楼有感
    雪后暖阳
    满城尽添黄金装
    敢叫岁月不冬天
  • 原文地址:https://www.cnblogs.com/aitiknowledge/p/15958876.html
Copyright © 2020-2023  润新知