欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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发起注销请求

  2. 系统1根据用户与系统1建立的会话id拿到令牌,向sso认证中心发起注销请求

  3. sso认证中心校验令牌有效,销毁全局会话

  4. sso认证中心向所有注册系统发起注销请求

  5. sso认证中心引导用户至登录页面

相关标签: java