sso单点登录的原理详解
程序员文章站
2022-06-21 15:06:56
...
单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分。
单点登录
相比于单系统登录,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。
sso-client
@Controller
public class HelloController {
@Value("${sso.server.url}")
private String ssoServerUrl;
/**
* 无需登录就可以访问
* @return
*/
@ResponseBody
@RequestMapping("/hello")
public String hello() {
return "hello";
}
/**
* 需要验证的连接
* @param model
* @param token 只要是ssoserver登陆成功回来就会带上
* @return
*/
@GetMapping("/employees")
public String employees(Model model, HttpSession session,@RequestParam(value="token",required = false) String token) {
if (!StringUtils.isEmpty(token)) {
// 去ssoserver登录成功调回来就会带上
//TODO 1、去ssoserver获取当前token真正对应的用户信息
RestTemplate restTemplate = new RestTemplate();
// 使用restTemplate进行远程请求
ResponseEntity<String> forEntity =
restTemplate.getForEntity("http://ssoserver.com:8080/userInfo?token=" + token,String.class);
// 拿到数据
String body = forEntity.getBody();
// 设置到session中
session.setAttribute("loginUser",body);
}
Object loginUser = session.getAttribute("loginUser");
if (loginUser == null ){
// 没有登录重定向到登陆页面,并带上当前地址
return "redirect:" + ssoServerUrl + "?redirect_url=http://client1.com:8081/employees";
} else {
List<String> emps = new ArrayList<>();
emps.add("张三");
emps.add("李四");
model.addAttribute("emps",emps);
return "list";
}
}
}
sso-server
@Controller
public class LoginController {
@Autowired
StringRedisTemplate redisTemplate;
/**
* 根据token从redis中查询用户信息
* @param token
* @return
*/
@ResponseBody
@GetMapping("/userInfo")
public String userInfo(@RequestParam("token") String token) {
String s = redisTemplate.opsForValue().get(token);
return s;
}
@GetMapping("login.html")
public String login(@RequestParam("redirect_url") String url, Model model,
@CookieValue(value = "sso_token",required = false)String
sso_token) {
if (!StringUtils.isEmpty(sso_token)) {
//说明有人之前登录过,给浏览器留下了痕迹
return "redirect:" + url + "?token=" + sso_token;
}
// 添加url到model地址中,在前端页面进行取出
model.addAttribute("url",url);
return "login";
}
/**
* 登录
* @param username
* @param password
* @param url client端带过来的地址
* @return
*/
@PostMapping("/doLogin")
public String doLogin(@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam("url") String url,
HttpServletResponse response){
// 账号密码不为空
if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
// 登陆成功
// 把登录成功的用户存起来
String uuid = UUID.randomUUID().toString().replace("-","");
redisTemplate.opsForValue().set(uuid,username);
// 将uuid存入cookie
Cookie token = new Cookie("sso_token",uuid);
response.addCookie(token);
// 保存到cookie
return "redirect:" + url + "?token=" + uuid;
}
// 登录失败,展示登录页
return "login";
}
}
单点注销
sso认证中心一直监听全局会话的状态,一旦全局会话销毁,监听器将通知所有注册系统执行注销操作
-
用户向系统1发起注销请求
-
系统1根据用户与系统1建立的会话id拿到令牌,向sso认证中心发起注销请求
-
sso认证中心校验令牌有效,销毁全局会话
-
sso认证中心向所有注册系统发起注销请求
-
sso认证中心引导用户至登录页面