Analysis of single sign on implementation of spring cloud microservice based on redis cluster
brief introduction
This paper introduces how to implement single sign on in microservice architecture
Create three services:
General idea: each service is equipped with an interceptor to check whether there is a token in the cookie. If there is a token, it will be released. If there is no token, @ r_ 301_ 2417 @ log in to the unified authentication center service, and return to the intercepted service after successful login.
Build redis cluster service
Reference documents for setting up redis clusters
Build a unified certification center
Add annotation to main function
/** * 单点登录既要注册到服务注册中心,又要向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); } }
Consumer redis service and fuse
@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; } }
Login service
@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; } }
The contoller layer handles login services and login jumps
Login service
/** * 登录业务 * * @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"; }
Login jump
@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"; }
Build a service consumer: add an interceptor to judge whether the token is empty
Interceptor
public class WebAdminInterceptor implements HandlerInterceptor { @Autowired private RedisService redisService; @Override public boolean preHandle(HttpServletRequest request,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,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,Exception ex) throws Exception { } }
Configuring Interceptors
@Configuration public class WebAdminInterceptorConfig implements @R_502_320@ { //将拦截器设置为Bean,在拦截其中才能使用@AutoWired注解自动注入 @Bean WebAdminInterceptor webAdminInterceptor() { return new WebAdminInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webAdminInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/static"); } }
Write an interface arbitrarily and trigger the interceptor for test
@RequestMapping(value = {"/login"}) public String index(){ return "index"; }
The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.