• 谷粒商城11——认证服务、短信验证、Gitee-OAuth 社交登录、分布式session


    九、认证服务

    新建一个认证服务模块,并注册到nacos

    image-20220729135059035

    • 添加默认页面到templates/,添加静态资源到nginx/html/static

    • 配置本机域名映射

    • 配置网关路由

    • #将主机地址为auth.gulimall.com转发至gulimall-auth-server
      - id: gulimall_auth_host
        uri: lb://gulimall-auth-server
        predicates:
          - Host=auth.gulimall.com
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 编写各个html之间的跳转逻辑

    • 利用SpringMVC的视图控制器完成页面跳转

    • @Configuration
      public class MyWebConfig implements WebMvcConfigurer {
      
      
          /**
           * 视图映射,控制页面跳转
           * @param registry
           */
          @Override
          public void addViewControllers(ViewControllerRegistry registry) {
      
              registry.addViewController("/login.html").setViewName("login");
              registry.addViewController("/reg.html").setViewName("reg");
      
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

    1.短信验证码功能

    前端点击自动倒计时六十秒、发送验证码按钮

    <div class="register-box">
        <label class="other_label">验 证 码
            <input name=code maxlength="20" type="text" placeholder="请输入验证码" class="caa">
        label>
        <a id="sendCode" class="">点击发送验证码a>
        <div class="tips" style="color:red" th:text="${errors!=null?(#maps.containsKey(errors,'code')?errors.code:''):''}">
        div>
    div>
    
    $("#sendCode").click(function () {
    		//如果有disabled,说明最近已经点过
    		if($(this).hasClass("disabled")){
    
    		}else {
    			timeOutChangeStyle();
    			//发送验证码
    			var phone=$("#phoneNum").val();
    			$.get("/sms/sendCode?phone="+phone,function (data){
    				if (data.code!=0){
    					alert(data.msg);
    				}
    			})
    		}
    	})
    
    	let time = 60;
    	function timeOutChangeStyle() {
    		//开启倒计时后设置标志属性disable的
    		$("#sendCode").attr("class", "disabled");
    		if(time==0){
    			$("#sendCode").text("点击发送验证码");
    			time=60;
    			$("#sendCode").attr("class", "");
    		}else {
    			$("#sendCode").text(time+"s后再次发送");
    			time--;
    			setTimeout("timeOutChangeStyle()", 1000);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    2.短信验证模仿

    没有备案的服务器,参考各种云api文档。这里简单写一下流程。

    api-gateway-demo-sign-java/HttpUtils.java at master · aliyun/api-gateway-demo-sign-java (github.com)下载HttpUtils放到公共微服务模块

    新建一个短信发送组件,可以配置短信发送的参数

    package com.henu.soft.merist.thirdparty.component;
    
    import com.henu.soft.merist.common.utils.HttpUtils;
    import lombok.Data;
    import org.apache.http.HttpResponse;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @Data
    @ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
    @Controller
    public class SmsComponent {
    
        private String host;
        private String path;
        private String appcode;
    
        public void sendCode(String phone,String code) {
    //        String host = "http://dingxin.market.alicloudapi.com";
    //        String path = "/dx/sendSms";
            String method = "POST";
    //        String appcode = "你自己的AppCode";
            Map<String, String> headers = new HashMap<String, String>();
            //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
            headers.put("Authorization", "APPCODE " + appcode);
            Map<String, String> querys = new HashMap<String, String>();
            querys.put("mobile",phone);
            querys.put("param", "code:"+code);
            querys.put("tpl_id", "TP1711063");
            Map<String, String> bodys = new HashMap<String, String>();
    
    
            try {
                /**
                 * 重要提示如下:
                 * HttpUtils请从
                 * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
                 * 下载
                 *
                 * 相应的依赖请参照
                 * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
                 */
                HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
                System.out.println(response.toString());
                //获取response的body
                //System.out.println(EntityUtils.toString(response.getEntity()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    配置文件:

    spring:
      cloud:
        alicloud:
          sms:
            host: http://dingxin.market.alicloudapi.com
            path: /dx/sendSms
            appcode: #
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    controller:

    package com.henu.soft.merist.thirdparty.controller;
    
    import com.henu.soft.merist.common.utils.R;
    import com.henu.soft.merist.thirdparty.component.SmsComponent;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/sms")
    public class SmsSendController {
        
        @Autowired
        SmsComponent smsComponent;
        
        /**
         * 提供给别的服务进行调用
         * @param phone
         * @param code
         * @return
         */
        @ResponseBody
        @GetMapping(value = "/sendCode")
        public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code) {
    
            //发送验证码
    //        smsComponent.sendCode(phone,code);
            System.out.println(phone+code);
            return R.ok();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    3.验证码生成远程调用发送短信

    由于验证码生成在认证模块,而短信发送验证码在第三方模块,所以需要feign远程调用

    • 导入open-feign的依赖,加上@EnableFeignClients 开启feign服务

    • 编写接口远程调用

    • package com.henu.soft.merist.auth.feign;
      
      import com.henu.soft.merist.common.utils.R;
      import org.springframework.cloud.openfeign.FeignClient;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestParam;
      import org.springframework.web.bind.annotation.ResponseBody;
      
      @FeignClient("gulimall-third-party")
      public interface ThreadPartyFeignService {
      
          @ResponseBody
          @GetMapping("/sms/sendCode")
          public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

    由于向手机发送验证码服务是假的 所以这里在控制台输出

    验证码生成+远程调用:

    package com.henu.soft.merist.auth.controller;
    
    import com.henu.soft.merist.auth.feign.ThreadPartyFeignService;
    import com.henu.soft.merist.common.utils.R;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import java.util.UUID;
    
    @Controller
    public class LoginController {
    
        @Autowired
        ThreadPartyFeignService threadPartyFeignService;
    
        /** 发送一个请求直接跳转到另一个页面
         * SpringMVC viewcontroller:将请求和页面映射过来
         */
        @GetMapping("/sms/sendCode")
        public R sendCode(@RequestParam("phone") String phone){
            String code = UUID.randomUUID().toString().substring(0, 5);
            //由于向手机发送验证码服务是假的 所以这里在控制台输出
            System.out.println("=====================code:"+code+"=====================");
            threadPartyFeignService.sendCode(phone, code);
            return R.ok();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    获取到验证码

    image-20220729201131561

    4.验证码防刷检验

    这里要解决三个问题:

    1. 验证码的过期时间设置,同时完善注册时验证码的校验
    2. 验证码的路径是暴露的,会受到恶意人员不停地调用耗费资源
    3. 尽管发送验证码之后有60s的倒计时,但是刷新页面,重新输入手机号又可以发送验证码

    image-20220729201714172

    4.1 验证码的校验

    这里由于验证码肯定不是永久有效的,所以将验证码存储到 redis 中。

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    redis 配置

    spring.redis.host=192.168.137.128
    spring.redis.port=6379
    
    • 1
    • 2

    设置验证码十分钟内有效

    @RestController
    public class LoginController {
    
        @Autowired
        ThreadPartyFeignService threadPartyFeignService;
    
        @Autowired
        StringRedisTemplate stringRedisTemplate;
    
        /** 发送一个请求直接跳转到另一个页面
         * SpringMVC viewcontroller:将请求和页面映射过来
         */
        @GetMapping("/sms/sendCode")
        public R sendCode(@RequestParam("phone") String phone){
            String code = UUID.randomUUID().toString().substring(0, 5);
            //由于向手机发送验证码服务是假的 所以这里在控制台输出
            System.out.println("=====================code:"+code+"=====================");
    
            //存储 手机号k + 验证码v 到redis  //public static final String SMS_CODE_CACHE_PREFIX = "sms:code:";
            //同时设置 在redis 中的过期时间  设置十分钟内有效
            stringRedisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX+phone,code,10,TimeUnit.MINUTES);
            threadPartyFeignService.sendCode(phone, code);
            return R.ok();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    4.2、4.3 验证码防刷

    • 为了防止验证码一直被刷,在 redis 存储的时候,加上时间
    • 每次获取验证码之前都要判断是否在 redis 中存在对应的key
      • 如果存在,判断是否过了60s,
        • 没过60s,提示60s后才能发送
        • 过了60s,发送验证码
      • 如果不存在,发送验证码

    修改代码:

    @GetMapping("/sms/sendCode")
    public R sendCode(@RequestParam("phone") String phone){
    
        String redisCode = stringRedisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX+phone);
        if (!StringUtils.isEmpty(redisCode)){
            long time = Long.parseLong(redisCode.split("_")[1]);
            if (System.currentTimeMillis() - time < 60000){
                return R.error(BizCodeEnume.SMS_CODE_EXCEPTION.getCode(),BizCodeEnume.SMS_CODE_EXCEPTION.getMsg());
            }
        }
    
        String code = UUID.randomUUID().toString().substring(0, 5) + "_" + System.currentTimeMillis();
        //由于向手机发送验证码服务是假的 所以这里在控制台输出
        System.out.println("=====================code:"+code+"=====================");
    
        //存储 手机号k + 验证码v 到redis  //public static final String SMS_CODE_CACHE_PREFIX = "sms:code:";
        //同时设置 在redis 中的过期时间  设置十分钟内有效
        stringRedisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX+phone,code,10,TimeUnit.MINUTES);
        threadPartyFeignService.sendCode(phone, code);
        return R.ok();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    另外就是,这里可以大量伪造手机号,进行访问,应该在获取验证码的时候加上图形验证。

    5.注册功能

    5.1 封装表单vo

    新建一个 UserRegisterVo 封装提交表单的数据,同时进行参数校验。

    使用@Long 校验长度,引入 hibernate.validator 参数校验。

    <dependency>
        <groupId>org.hibernate.validatorgroupId>
        <artifactId>hibernate-validatorartifactId>
        <version>6.0.13.Finalversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    UserRegisterVo:

    package com.henu.soft.merist.auth.vo;
    
    import lombok.Data;
    import org.hibernate.validator.constraints.Length;
    import javax.validation.constraints.NotEmpty;
    import javax.validation.constraints.Pattern;
    
    @Data
    public class UserRegisterVo {
        @NotEmpty(message = "用户名必须提交")
        @Length(min = 6,max = 18,message="用户名必须是6-18位")
        private String userName;
    
        @NotEmpty(message = "密码必须填写")
        @Length(min = 6,max = 18,message="密码必须是6-18位")
        private String password;
    
        @NotEmpty(message = "手机号必须填写")
        @Pattern(regexp = "^[1]([3-9])[0-9]{9}$",message = "手机号格式不正确")
        private String phoneNum;
    
        @NotEmpty(message = "用户名必须填写")
        private String code;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5.2 认证服务调用会员服务

    校验验证码,并且远程调用会员服务进行注册(在数据库中插入数据)

    @PostMapping("/register")
        public String register(@Valid UserRegisterVo vo, BindingResult result, RedirectAttributes redirectAttributes){
            if (result.hasErrors()){
                Map<String, String> errors = result.getFieldErrors().stream().collect(
                        Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage)
                );
    
    //            model.addAttribute("errors",errors);
                redirectAttributes.addFlashAttribute("errors",errors);
                //校验出错,转发到注册页
                return "redirect:http://auth.gulimall.com/reg.html";
            }
    
            //1.输入的验证码
            String code = vo.getCode();
            //获取存入redis中的验证码
            String redisCode = stringRedisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
            if (!StringUtils.isEmpty(code)){
                if (code.equals(redisCode.split("_")[0])){
                    //验证通过
                    //删除验证码,令牌机制
                    stringRedisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
                    //进行真正的注册
                    R register = memberFeignService.register(vo);
                    if (register.getCode() == 0){
                        //成功
                        return "redirect:http://auth.gulimall.com/login.html";
                    }else {
                        //失败
                        Map<String,String> errors = new HashMap<>();
                        TypeReference<String> typeReference = new TypeReference<String>(){};
                        String msg = register.getData(typeReference);
                        errors.put("msg",msg);
                        redirectAttributes.addFlashAttribute("errors",errors);
                        return "redirect:http://auth.gulimall.com/reg.html";
                    }
                }
            }
            //输入验证码为空 or 校验验证码失败  走到这里
            //校验出错回到注册页面
            Map<String, String> errors = new HashMap<>();
            errors.put("code","验证码错误");
            redirectAttributes.addFlashAttribute("errors",errors);
            return "redirect:http://auth.gulimall.com/reg.html";
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    @FeignClient("gulimall-member")
    public interface MemberFeignService {
        @PostMapping("/register")
        public R register(@RequestBody UserRegisterVo userRegisterVo);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.3 会员服务 注册

    这一步包括 检验手机号、用户名是否被占用,密码MD5盐值加密,向数据库插入数据。

    MD5&MD5盐值加密

    • MD5:
      • Message Digest algorithm 5,信息摘要算法
        • 压缩性:任意长度的数据,算出的MD5值长度都是固定的
        • 容易计算:从原数据计算出MD5值很容易
        • 抗修改性:对原数据进行任何改动,哪怕只修改一个字节,所得到的MD5值有很大区别
        • 强碰撞性:想找到两个不同的数据,使它们具有相同的MD5,是非常困难的
    • 加盐:
      • 通过生成随机数与 MD5 生化字符串进行组合
      • 数据库同时存储 MD5 值与 salt 值,验证正确性时用 salt 进行 MD5 即可
    @Override
    public void register(MemberRegisterVo vo) {
        //1.检查号码是否唯一
        checkPhoneUnique(vo.getPhone());
        //2.检查用户名是否唯一
        checkUserNameUnique(vo.getUserName());
        //3.信息唯一 进行插入
        MemberEntity memberEntity = new MemberEntity();
        //3.1 保存基本信息
        memberEntity.setUsername(vo.getUserName());
        memberEntity.setMobile(vo.getPhone());
        memberEntity.setPassword(vo.getPassword());
        //3.2 加密保存密码
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encodePassword = passwordEncoder.encode(vo.getPassword());
        memberEntity.setPassword(encodePassword);
        //3.3 设置会员默认等级
        //3.3.1 找到会员默认等级
        MemberLevelEntity defaultLevel = memberLevelService.getOne(
                new QueryWrapper<MemberLevelEntity>().eq("default_status", 1));
        //3.3.2 设置会员等级为默认
        memberEntity.setLevelId(defaultLevel.getId());
        //4. 保存用户信息
        this.save(memberEntity);
    }
    
    private void checkPhoneUnique(String phone) throws PhoneNumExistException{
        Long count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone));
        if (count > 0){
            throw new PhoneNumExistException();
        }
    }
    private void checkUserNameUnique(String username) throws UserNameExistException{
        Long count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("username", username));
        if (count > 0){
            throw new UserNameExistException();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    6.登录功能

    6.1 普通账号密码登录

    认证模块:

    controller:

    @PostMapping("/login")
    public String login(UserLoginVo loginVo, RedirectAttributes redirectAttributes, HttpSession session){
        R login = memberFeignService.login(loginVo);
        if (login.getCode() == 0){
            String jsonString = JSON.toJSONString(login.get("memberEntity"));
            MemberResponseTo memberResponseTo = JSON.parseObject(jsonString, new TypeReference<MemberResponseTo>() {
            });
            //public static final String LOGIN_USER = "loginUser";
            session.setAttribute(AuthServerConstant.LOGIN_USER,memberResponseTo);
            return "redirect:http://gulimall.com";
        }else {
            String msg = (String) login.get("msg");
            Map<String,String> errors = new HashMap<>();
            errors.put("msg",msg);
            redirectAttributes.addFlashAttribute("errors",errors);
            return "redirect:http://auth.gulimall.com/login.html";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    feign:

    @FeignClient("gulimall-member")
    public interface MemberFeignService {
        @PostMapping("/member/member/register")
        public R register(@RequestBody UserRegisterVo userRegisterVo);
    
        @PostMapping("/member/member//login")
        public R login(@RequestBody UserLoginVo userLoginVo);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    member 会员模块:

    controller:

    /**
     * 登录
     */
    @PostMapping("/login")
    public R login(@RequestBody MemberLoginVo memberLoginVo){
        MemberEntity entity = new MemberEntity();
        try {
            entity = memberService.login(memberLoginVo);
        }catch (PasswordWrongException e){
            // PASSWORD_WRONG_EXCEPTION(15004,"密码错误");
            return R.error(BizCodeEnume.PASSWORD_WRONG_EXCEPTION.getCode(), BizCodeEnume.PASSWORD_WRONG_EXCEPTION.getMsg());
        }
        return entity == null ?
            // USER_NOT_EXIST_EXCEPTION(15003,"用户不存在"),
         	R.error(BizCodeEnume.USER_NOT_EXIST_EXCEPTION.getCode(),BizCodeEnume.USER_NOT_EXIST_EXCEPTION.getMsg())
            : R.ok().put("memberEntity",entity);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    serviceImpl:

    @Override
    public MemberEntity login(MemberLoginVo vo) {
        //账号:手机号 or 用户名
        String account = vo.getAccount();
        MemberEntity memberEntity = this.getOne(new QueryWrapper<MemberEntity>().eq("username", account).or().eq("mobile", account));
        if (memberEntity != null){
            checkPassword(memberEntity,vo.getPassword());
            return memberEntity;
        }
        return null;
    }
    
    @Override
    public void checkPassword(MemberEntity memberEntity, String password) throws PasswordWrongException {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if (!passwordEncoder.matches(password,memberEntity.getPassword())){
            throw new PasswordWrongException();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    6.2 OAuth2.0-社交登录

    参考博客:OAuth2.0 社交登录-Gitee springboot项目整合(微服务分布式)完整代码包括 数据库、前、后端_HotRabbit.的博客-CSDN博客

    6.3 分布式Session -Spring Sesion原理、实战

    参考博客:分布式session ——Spring Session原理、实战解决 子域之间的 session 共享问题、不同服务器 session 共享解决方案_HotRabbit.的博客-CSDN博客

    7.单点登录 SSO

    核心

    • 三个系统即使域名不同,也要给三个系统同步同一认证状态
    • 中央认证服务器、其他系统想要登录去中央服务器登录,登录成功之后跳转回来

    image-20220803134219056

  • 相关阅读:
    Virtink:更轻量的 Kubernetes 原生虚拟化管理引擎
    pytorch Nvidia 数据预处理加速
    使用Spyder进行动态网页爬取:实战指南
    【前段基础入门之】=>初识 HTML
    基于SpringBoot的校园周边美食探索及分享平台的设计与实现
    QT6不支持QDesktopWidget包含头文件报错Qt 获取设备屏幕大小
    【无标题】
    jenkins流水线实现xjar加固
    【数学建模】图论模型(基础理论+最大流与最小费用流问题)
    Vue组合式 api 的常用知识点
  • 原文地址:https://blog.csdn.net/qq_52476654/article/details/126138876