zoukankan      html  css  js  c++  java
  • springcloud微服务基于redis集群的单点登录

    springcloud微服务基于redis集群的单点登录

    yls
    2019-9-23


    简介

    本文介绍微服务架构中如何实现单点登录功能
    创建三个服务:

    1. 操作redis集群的服务,用于多个服务之间共享数据
    2. 统一认证中心服务,用于整个系统的统一登录认证
    3. 服务消费者,用于测试单点登录

    大体思路:每个服务都设置一个拦截器检查cookie中是否有token,若有token,则放行,若没有token,重定向到统一认证中心服务进行登录,登录成功后返回到被拦截的服务。

    搭建redis集群服务

    搭建redis集群参考文档

    搭建统一认证中心

    • 主函数添加注解
    /**
     * 单点登录既要注册到服务注册中心,又要向redis服务系统获取鼓舞
     * 所以要添加 @EnableDiscoveryClient   @EnableEurekaClient 两个注解
     *
     */
    
    @EnableDiscoveryClient
    @EnableEurekaClient
    @EnableFeignClients
    @MapperScan(basePackages = "com.example.itokenservicesso.mapper")
    @SpringBootApplication
    public class ItokenServiceSsoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ItokenServiceSsoApplication.class, args);
        }
    
    }
    
    • 消费redis服务和熔断器
    @FeignClient(value = "itoken-service-redis", fallback = RedisServiceFallBack.class)
    public interface RedisService {
    
        @PostMapping(value = "put")
        public String put(@RequestParam(value = "key") String key, @RequestParam(value = "value") String value, @RequestParam(value = "seconds") long seconds);
    
        @GetMapping(value = "get")
        public String get(@RequestParam(value = "key") String key);
    
    }
    
    @Component
    public class RedisServiceFallBack implements RedisService {
        @Override
        public String put(String key, String value, long seconds) {
            return FallBack.badGateWay();
        }
    
        @Override
        public String get(String key) {
            return FallBack.badGateWay();
        }
    }
    
    public class FallBack {
    
        public static String badGateWay(){
            try {
                return JsonUtil.objectToString(ResultUtil.error(502,"内部错误"));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    • 登录服务
    @Service
    public class LoginServiceImpl implements LoginService {
    
    
        @Autowired
        private UserMapper userMapper;
    
        @Autowired
        private RedisService redisService;
    
        @Override
        public User login(String loginCode, String plantPassword) {
            //从缓存中获取登录用户的数据
            String json = redisService.get(loginCode);
    
            User user = null;
            //如果缓存中没有数据,从数据库取数据
            if (json == null) {
                user = userMapper.selectAll(loginCode);
                String passwordMd5 = DigestUtils.md5DigestAsHex(plantPassword.getBytes());
                if (user != null && passwordMd5.equals(user.getPassword())) {
                    //登录成功,刷新缓存
                    try {
                        redisService.put(loginCode, JsonUtil.objectToString(user), 60 * 60 * 24);
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }
                    return user;
                } else {
                    return null;
                }
            }
            //如果缓存中有数据
            else {
                try {
                    user = JsonUtil.stringToObject(json, User.class);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return user;
        }
    }
    
    • contoller层,处理登录业务和登录跳转

      • 登录业务

            /**
            * 登录业务
            *
            * @param loginCode
            * @param password
            * @return
            */
            @PostMapping("login")
            public String login(String loginCode,
                                String password,
                                @RequestParam(required = false) String url,
                                HttpServletRequest request,
                                HttpServletResponse response,
                                RedirectAttributes redirectAttributes) {
                User user = loginService.login(loginCode, password);
                //登录成功
                if (user != null) {
        
                    String token = UUID.randomUUID().toString();
                    //将token放入缓存
                    String result = redisService.put(token, loginCode, 60 * 60 * 24);
                    //如果redisService没有熔断,也就是返回ok,才能执行
                    if (result != null && result.equals("ok")) {
                        CookieUtil.setCookie(response, "token", token, 60 * 60 * 24);
                        if (url != null && !url.trim().equals(""))
                            return "redirect:" + url;
                    }
                    //熔断后返回错误提示
                    else {
                        redirectAttributes.addFlashAttribute("message", "服务器异常");
                    }
        
                }
                //登录失败
                else {
                    redirectAttributes.addFlashAttribute("message", "用户名或密码错误");
                }
                return "redirect:/login";
            }
        
      • 登录跳转

          @Autowired
          private LoginService loginService;
      
          @Autowired
          private RedisService redisService;
      
          /**
          * 跳转登录页
          */
          @GetMapping("login")
          public String login(HttpServletRequest request,
                              Model model,
                              @RequestParam(required = false) String url
          ) {
              String token = CookieUtil.getCookie(request, "token");
              //token不为空可能已登录,从redis获取账号
              if (token != null && token.trim().length() != 0) {
                  String loginCode = redisService.get(token);
                  //如果账号不为空,从redis获取该账号的个人信息
                  if (loginCode != null && loginCode.trim().length() != 0) {
                      String json = redisService.get(loginCode);
                      if (json != null && json.trim().length() != 0) {
                          try {
                              User user = JsonUtil.stringToObject(json, User.class);
      
                              //已登录
                              if (user != null) {
                                  if (url != null && url.trim().length() != 0) {
                                      return "redirect:" + url;
                                  }
                              }
                              //将登录信息传到登录页
                              model.addAttribute("user", user);
      
                          } catch (IOException e) {
                              e.printStackTrace();
                          }
      
                      }
                  }
              }
              return "login";
          }
      
    • 搭建服务消费者:添加一个拦截器,判断token是否为空

      • 拦截器
      public class WebAdminInterceptor implements HandlerInterceptor {
      
          @Autowired
          private RedisService redisService;
      
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              String token = CookieUtil.getCookie(request, "token");
      
              //token为空,一定没有登录
              if (token == null || token.isEmpty()) {
                  response.sendRedirect("http://localhost:8503/login?url=http://localhost:8601/login");
                  return false;
              }
              return true;
          }
      
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
      
              HttpSession session = request.getSession();
              User user = (User) session.getAttribute("user");
      
              //已登陆状态
              if (user != null) {
                  if (modelAndView != null) {
                      modelAndView.addObject("user", user);
      
                  }
              }
              //未登录状态
              else {
                  String token = CookieUtil.getCookie(request, "token");
                  if (token != null && !token.isEmpty()) {
                      String loginCode = redisService.get(token);
      
                      if (loginCode != null && !loginCode.isEmpty()) {
                          String json = redisService.get(loginCode);
                          if (json != null && !json.isEmpty()) {
                              //已登录状态,创建局部会话
                              user = JsonUtil.stringToObject(json, User.class);
                              if (modelAndView != null) {
                                  modelAndView.addObject("user", user);
                              }
                              request.getSession().setAttribute("user", user);
                          }
                      }
                  }
              }
      
              //二次确认是否有用户信息
              if (user == null) {
                  response.sendRedirect("http://localhost:8503/login?url=http://localhost:8601/login");
      
              }
      
          }
      
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
      
          }
      }
      
      • 配置拦截器
      @Configuration
      public class WebAdminInterceptorConfig implements WebMvcConfigurer {
      
          //将拦截器设置为Bean,在拦截其中才能使用@AutoWired注解自动注入
          @Bean
          WebAdminInterceptor webAdminInterceptor() {
              return new WebAdminInterceptor();
          }
      
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              registry.addInterceptor(webAdminInterceptor())
                      .addPathPatterns("/**")
                      .excludePathPatterns("/static");
          }
      }
      
    • 任意写一个接口,触发拦截器进行测试

    @RequestMapping(value = {"/login"})
        public String index(){
            return "index";
        }
    
  • 相关阅读:
    Linux 下的类似Windows下Everything的搜索工具
    windows和linux环境下制作U盘启动盘
    程序调试手段之gdb, vxworks shell
    LeetCode 1021. Remove Outermost Parentheses (删除最外层的括号)
    LeetCode 1047. Remove All Adjacent Duplicates In String (删除字符串中的所有相邻重复项)
    LeetCode 844. Backspace String Compare (比较含退格的字符串)
    LeetCode 860. Lemonade Change (柠檬水找零)
    LeetCode 1221. Split a String in Balanced Strings (分割平衡字符串)
    LeetCode 1046. Last Stone Weight (最后一块石头的重量 )
    LeetCode 746. Min Cost Climbing Stairs (使用最小花费爬楼梯)
  • 原文地址:https://www.cnblogs.com/yloved/p/11574211.html
Copyright © 2011-2022 走看看