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";
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM