springcloud微服务基于redis集群的单点登录实现解析
简介
本文介绍微服务架构中如何实现单点登录功能
创建三个服务:
- 操作redis集群的服务,用于多个服务之间共享数据
- 统一认证中心服务,用于整个系统的统一登录认证
- 服务消费者,用于测试单点登录
大体思路:每个服务都设置一个拦截器检查cookie中是否有token,若有token,则放行,若没有token,重定向到统一认证中心服务进行登录,登录成功后返回到被拦截的服务。
搭建redis集群服务
搭建redis集群参考文档
搭建统一认证中心
主函数添加注解
/** *单点登录既要注册到服务注册中心,又要向redis服务系统获取鼓舞 *所以要添加@EnableDiscoveryClient@EnableEurekaClient两个注解 * */ @EnableDiscoveryClient @EnableEurekaClient @EnableFeignClients @MapperScan(basePackages="com.example.itokenservicesso.mapper") @SpringBootApplication publicclassItokenServiceSsoApplication{ publicstaticvoidmain(String[]args){ SpringApplication.run(ItokenServiceSsoApplication.class,args); } }
消费redis服务和熔断器
@FeignClient(value="itoken-service-redis",fallback=RedisServiceFallBack.class) publicinterfaceRedisService{ @PostMapping(value="put") publicStringput(@RequestParam(value="key")Stringkey,@RequestParam(value="value")Stringvalue,@RequestParam(value="seconds")longseconds); @GetMapping(value="get") publicStringget(@RequestParam(value="key")Stringkey); }
@Component publicclassRedisServiceFallBackimplementsRedisService{ @Override publicStringput(Stringkey,Stringvalue,longseconds){ returnFallBack.badGateWay(); } @Override publicStringget(Stringkey){ returnFallBack.badGateWay(); } }
publicclassFallBack{ publicstaticStringbadGateWay(){ try{ returnJsonUtil.objectToString(ResultUtil.error(502,"内部错误")); }catch(JsonProcessingExceptione){ e.printStackTrace(); } returnnull; } }
登录服务
@Service publicclassLoginServiceImplimplementsLoginService{ @Autowired privateUserMapperuserMapper; @Autowired privateRedisServiceredisService; @Override publicUserlogin(StringloginCode,StringplantPassword){ //从缓存中获取登录用户的数据 Stringjson=redisService.get(loginCode); Useruser=null; //如果缓存中没有数据,从数据库取数据 if(json==null){ user=userMapper.selectAll(loginCode); StringpasswordMd5=DigestUtils.md5DigestAsHex(plantPassword.getBytes()); if(user!=null&&passwordMd5.equals(user.getPassword())){ //登录成功,刷新缓存 try{ redisService.put(loginCode,JsonUtil.objectToString(user),60*60*24); }catch(JsonProcessingExceptione){ e.printStackTrace(); } returnuser; }else{ returnnull; } } //如果缓存中有数据 else{ try{ user=JsonUtil.stringToObject(json,User.class); }catch(IOExceptione){ e.printStackTrace(); } } returnuser; } }
contoller层,处理登录业务和登录跳转
登录业务
/** *登录业务 * *@paramloginCode *@parampassword *@return */ @PostMapping("login") publicStringlogin(StringloginCode, Stringpassword, @RequestParam(required=false)Stringurl, HttpServletRequestrequest, HttpServletResponseresponse, RedirectAttributesredirectAttributes){ Useruser=loginService.login(loginCode,password); //登录成功 if(user!=null){ Stringtoken=UUID.randomUUID().toString(); //将token放入缓存 Stringresult=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 privateLoginServiceloginService; @Autowired privateRedisServiceredisService; /** *跳转登录页 */ @GetMapping("login") publicStringlogin(HttpServletRequestrequest, Modelmodel, @RequestParam(required=false)Stringurl ){ Stringtoken=CookieUtil.getCookie(request,"token"); //token不为空可能已登录,从redis获取账号 if(token!=null&&token.trim().length()!=0){ StringloginCode=redisService.get(token); //如果账号不为空,从redis获取该账号的个人信息 if(loginCode!=null&&loginCode.trim().length()!=0){ Stringjson=redisService.get(loginCode); if(json!=null&&json.trim().length()!=0){ try{ Useruser=JsonUtil.stringToObject(json,User.class); //已登录 if(user!=null){ if(url!=null&&url.trim().length()!=0){ return"redirect:"+url; } } //将登录信息传到登录页 model.addAttribute("user",user); }catch(IOExceptione){ e.printStackTrace(); } } } } return"login"; }
搭建服务消费者:添加一个拦截器,判断token是否为空
拦截器
publicclassWebAdminInterceptorimplementsHandlerInterceptor{ @Autowired privateRedisServiceredisService; @Override publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{ Stringtoken=CookieUtil.getCookie(request,"token"); //token为空,一定没有登录 if(token==null||token.isEmpty()){ response.sendRedirect("http://localhost:8503/login?url=http://localhost:8601/login"); returnfalse; } returntrue; } @Override publicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{ HttpSessionsession=request.getSession(); Useruser=(User)session.getAttribute("user"); //已登陆状态 if(user!=null){ if(modelAndView!=null){ modelAndView.addObject("user",user); } } //未登录状态 else{ Stringtoken=CookieUtil.getCookie(request,"token"); if(token!=null&&!token.isEmpty()){ StringloginCode=redisService.get(token); if(loginCode!=null&&!loginCode.isEmpty()){ Stringjson=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 publicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{ } }
配置拦截器
@Configuration publicclassWebAdminInterceptorConfigimplementsWebMvcConfigurer{ //将拦截器设置为Bean,在拦截其中才能使用@AutoWired注解自动注入 @Bean WebAdminInterceptorwebAdminInterceptor(){ returnnewWebAdminInterceptor(); } @Override publicvoidaddInterceptors(InterceptorRegistryregistry){ registry.addInterceptor(webAdminInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/static"); } }
任意写一个接口,触发拦截器进行测试
@RequestMapping(value={"/login"}) publicStringindex(){ return"index"; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。