流程图
参数
不同域名之下(不同父域名)
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_token
if (!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);
//向客户端浏览器写入token
Cookie 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的用户信息,调用缓存中系统的退出接口,清除每个客户端的局部会话,接着跳转登录页面。