流程图
参数
不同域名之下(不同父域名)
cookie+session+redis
流程追踪
用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
sso认证中心发现用户未登录,将用户引导至登录页面
用户输入用户名密码提交登录申请s
so认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌。
//client携带redirect_url来这里请求我们的认证//那么需要再认证页面上保存下来redirect_url//@CookieValue(value = "sso_token", required = false)@GetMapping("/login.html")public String loginPage(@RequestParam("redirect_url") String url, Model model, @CookieValue(value = "sso_token", required = false) String sso_token) {//如果存在sso_tokenif (!StringUtils.isEmpty(sso_token)) {return "redirect:" + url + "?token=" + sso_token;}//知道下个页面跳转的位置model.addAttribute("url", url);return "login";}@ResponseBody@GetMapping("/userinfo")public String userinfo(@RequestParam(value = "token") String token) {String s = stringRedisTemplate.opsForValue().get(token);return s;}@PostMapping("/doLogin")public String doLogin(@RequestParam("username") String username, @RequestParam("password") String password, @RequestParam("redirect_url") String url, HttpServletResponse response) {//如果想要退出登录那么sso_token失效if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)){//颁发token 并且将信息存放到redis中String uuid = UUID.randomUUID().toString().replaceAll("-","");stringRedisTemplate.opsForValue().set(uuid,username);//向客户端浏览器写入tokenCookie cookie = new Cookie("sso_token",uuid);response.addCookie(cookie);//将cookie写到浏览器//原因是如果第一个服务已经登录,第二个服务来登录,检测到没有session没有token来进行认证//login检测到cookie中有登录状态就应该通过return "redirect:" + url + "?token=" + uuid;}return "login";}
sso认证中心带着令牌跳转会最初的请求地址(系统1)
系统1拿到令牌,去sso认证中心校验令牌是否有效
sso认证中心校验令牌,返回有效,注册系统1
系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源
@GetMapping(value = "/employees")public String employees(Model model, HttpSession session, @RequestParam(value = "token", required = false) String token) {//如果token存在if (!StringUtils.isEmpty(token)) {RestTemplate restTemplate = new RestTemplate();//根据token获取其中的用户信息放到session当中ResponseEntity<String> forEntity = restTemplate.getForEntity("http://ssoserver.com:8080/userinfo?token=" + token, String.class);String body = forEntity.getBody();session.setAttribute("loginUser", body);}//session是否存在Object user = session.getAttribute("loginUser");if (user == null) {//进入认证中心进行登录 并且指定登录成功之后的重定向页面return "redirect:" + "http://ssoserver.com:8080/login.html" + "?redirect_url=http://client1.com:8082/employees";} else {//返回信息List<String> res = new ArrayList<>();res.add("张三");res.add("李四");model.addAttribute("emps", res);return "employees";}}
用户访问系统2的受保护资源
系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
系统2拿到令牌,去sso认证中心校验令牌是否有效
sso认证中心校验令牌,返回有效,注册系统2
系统2使用该令牌创建与用户的局部会话,返回受保护资源用户登录成功之后,会与sso认证中心及各个子系统建立会话,用户与sso认证中心建立的会话称为全局会话
用户与各个子系统建立的会话称为局部会话,局部会话建立之后,用户访问子系统受保护资源将不再通过sso认证中心
分析
每个系统中都会存在一份cookie,并且在第一次登录的时候系统要去访问用户中心获取令牌,之后就是session。
当用户退出系统,需要做的是销毁局部会话以及cookie,重定向到认证中心的退出接口,认证中心从会话中获取令牌,销毁全局会话,清除redis中该token的用户信息,调用缓存中系统的退出接口,清除每个客户端的局部会话,接着跳转登录页面。