SSO单点登录的简单实现

本文详细介绍了单点登录(SSO)的实现过程,包括登录服务器设置、客户端处理以及整个登录流程。通过在登录服务器上保存用户痕迹,并将痕迹信息通过URL传递给其他系统,使得用户只需登录一次即可访问多个服务,提高了用户体验。同时,文章提供了开源SSO项目的链接,便于读者深入理解并实践。
摘要由CSDN通过智能技术生成


前言

很早期的公司,一家公司可能只有一个Server,慢慢的Server开始变多了。每个Server都要进行注册登录,退出的时候又要一个个退出。用户体验很不好!你可以想象一下,上豆瓣 要登录豆瓣FM、豆瓣读书、豆瓣电影、豆瓣日记…真的会让人崩溃的。我们想要另一种登录体验:一家企业下的服务只要一次注册,登录的时候只要一次登录,退出的时候只要一次退出。怎么做?

接下来就实现单点登录的功能。

流程图
在这里插入图片描述

实现

开源sso的项目:https://gitee.com/xuxueli0323/xxl-sso

流程

  1. 给登录服务器留下痕迹
  2. 登录服务器要将token信息重定向的时候,带到url地址上
  3. 其他系统要处理url地址上的token,只要有将token对应的用户保存到自己的session
  4. 自己系统将用户保存在自己的session中

server:登录服务器、8080 、sso.com
web-sample1:项目1 、8081 、client1.com
web-sample2:项目1 、8082 、client2.com

假设发送这一个请求 sso.com:8080/login.html?redirect_url=http://client1.com:8081/employees

登录服务器sso.com

    @GetMapping("/login.html") 
    //正常来说是调用服务而不是在参数填写返回地址的,为了图方便就这样了
    public String loginPage(@RequestParam("redirect_url") String url, Model model, @CookieValue(value = "sso_token", required = false) String sso_token 
    //获取浏览器session信息) {
    
        //判断浏览器是否有登录痕迹
        if (!StringUtils.isEmpty(sso_token)) {
            return "redirect:" + url + "?token=" + sso_token;
        }
        //无痕迹返回
        model.addAttribute("url", url);

        return "login";
    }
    
   @PostMapping(value = "/doLogin")
    public String doLogin(@RequestParam("username") String username, @RequestParam("password") String password, @RequestParam("redirect_url") String url, HttpServletResponse response) {

        //登录成功跳转
        if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {

            String uuid = UUID.randomUUID().toString().replace("_", "");
            //保存痕迹
            redisTemplate.opsForValue().set(uuid, username);
            //保存痕迹的坐标
            Cookie sso_token = new Cookie("sso_token", uuid);

            response.addCookie(sso_token);
            //登录成功跳转新地址带上保存痕迹的坐标
            return "redirect:" + url + "?token=" + uuid;
        }
        //登录失败跳回到登录页
        return "login";
    }
    
    @ResponseBody
    @GetMapping("/userinfo") 获取保存的痕迹
    public String userinfo(@RequestParam(value = "token") String token) {
    	//获取保存的痕迹
        String s = redisTemplate.opsForValue().get(token);

        return s;

    }

登录页

<form action="/doLogin" method="post">
    用户名:<input type="text" name="username" /><br />
    密码:<input type="password" name="password" /><br />
     <!--无痕迹登录所携带地址-->
    <input type="hidden" name="redirect_url" th:value="${url}" />
    <input type="submit" value="登录">
</form>

客户端client1.com

 @GetMapping(value = "/employees")
    public String employees(Model model, HttpSession session, @RequestParam(value = "token", required = false) String token) {
   		 //拥有痕迹
        if (!StringUtils.isEmpty(token)) {
            RestTemplate restTemplate=new RestTemplate();
            //发送请求通过痕迹的坐标获取痕迹内容
            ResponseEntity<String> forEntity = restTemplate.getForEntity("http://sso.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:" + "http://sso.com:8080/login.html"+"?redirect_url=http://localhost:8081/employees";
        } else {

            List<String> emps = new ArrayList<>();

            emps.add("张三");
            emps.add("李四");

            model.addAttribute("emps", emps);
            return "employees";
        }
    }

成功页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>员工列表</title>
</head>
<body>
<h1>欢迎:[[${session.loginUser}]]</h1>
<ul>
    <li th:each="emp:${emps}">姓名:[[${emp}]]</li>
</ul>
</body>
</html>

以上流程走通以后已经给浏览器留下了痕迹
在这里插入图片描述

就算再来一个客户端来访问也会因为登录服务器的判断而直接去往成功页面,实现了单次登录的效果!!!

        //判断浏览器是否有登录痕迹
        if (!StringUtils.isEmpty(sso_token)) {
            return "redirect:" + url + "?token=" + sso_token;
        }
        //无痕迹返回
        model.addAttribute("url", url);

        return "login";
    }

通过session和类似令牌机制来实现这功能的思想确实很牛啊,当然大型业务就没那么简单了…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值