单点登录系统SSO是如何实现的?
原文在这里: 单点登录系统SSO是如何实现的?
所谓单点登录就是在A系统登录以后,跳转到B系统,此时可以直接访问B系统的资源,而不需要二次登录,目前这种需求已经非常普遍了,那么背后是怎么实现的呢?本文将用一个实际的例子来给大家详细的讲解下。
准备工作
(1)准备3个域名来模拟3个站点,www.site1.com和www.site2.com是业务域,www.usercenter.com是用户中心。一般单点登录系统背后都有一个独立的用户中心。
当然我们不需要真的去万网申请3个域名,只需要修改下本机的host文件即可,修改C:\Windows\System32\drivers\etc,添加以下内容:
127.0.0.1 www.site1.com
127.0.0.1 www.site2.com
127.0.0.1 www.usercenter.com
(2)准备一个nginx,把三个域名都挂上
我们本机启动3个tomcat,端口分别是site1:8081,site2:8082,usercenter:8080,通过nginx统一用标准的80来访问。
server {listen 80;server_name www.usercenter.com;location / {proxy_set_header Host $host;proxy_set_header X-Real-Ip $remote_addr;proxy_set_header X-Forwarded-For $remote_addr;proxy_pass http://127.0.0.1:8080;}}server {listen 80;server_name www.site1.com;location / {proxy_set_header Host $host;proxy_set_header X-Real-Ip $remote_addr;proxy_set_header X-Forwarded-For $remote_addr;proxy_pass http://127.0.0.1:8081;}}server {listen 80;server_name www.site2.com;location / {proxy_set_header Host $host;proxy_set_header X-Real-Ip $remote_addr;proxy_set_header X-Forwarded-For $remote_addr;proxy_pass http://127.0.0.1:8082;}}
现在我们就可以在本机用3个tomcat来模拟3个独立域名的服务器了。
原理分析
(1)浏览器访问site1,site1首先检查请求中是否带有site1的cookie,cookie的值是用户的token。如果有则向用户中心检查token的有效性,如果有效则可以直接访问site1,如果没有或者失效,重定向到用户中心去登录。
(2)浏览器进入用户中心的登录页面,用户输入用户名和密码,登录成功以后,用户中心需要写usercenter域的cookie,cookie的值是随机生成的一个token,然后重定向回到site1,同时把token作为url的参数给回传过去,因为cookie是usercenter域的,不是site1域的,只能通过url参数来传递。
(3)浏览器到了site1,还是首先检查cookie为空,然后需要检查参数中是否有用户中心设置的token参数,如果有,检查参数有限性,拿到用户信息,写site1的cookie,cookie的值是用户中心设置的token。
此时,如果浏览器继续访问site1的页面,会回传site1的cookie,只要去用户中心校验有效性即可。
如果,此时要跳转到site2,site2同样也要做:
(1)浏览器访问site2,site2首先检查请求中是否带有site2的cookie,cookie的值是用户的token。如果有则向用户中心检查token的有效性,如果有效则可以直接访问site2,如果没有或者失效,重定向到用户中心去登录。
(2)浏览器进入用户中心要做登录的时候,此时浏览器是传递了site1登录成功以后用户中心下发的cookie的,因为此时访问的是usercenter域,因此用户中心拿到cookie以后,只需要去校验有效性,校验通过则重定向回site2.
(3)浏览器到了site2,还是首先检查cookie为空,然后需要检查参数中是否有用户中心设置的token参数,如果有,检查参数有效性,拿到用户信息,写site2的cookie,cookie的值是用户中心设置的token。
此时,如果浏览器继续访问site2的页面,会回传site2的cookie,只要去用户中心校验有效性即可。
上面啰啰嗦嗦一大堆,看上去很绕很抽象,一团乱麻,还是来看代码吧,看代码就清爽多了,图就不画了,感觉帮助不大。
代码实现
本文我们用springboot+thymeleaf来实现,springmvc也是一样的道理。
site1访问需要登录的页面的时候:
@GetMapping("/main")public String main(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {//首先从cookie中取token,这里只能取site1的cookie//首次访问肯是空,除非后面登陆成功以后设置过String tk = getFromCookie(request);if(tk != null) {//验证token有效性String username = getFromUserCenter(tk);if(username != null) {model.addAttribute("username", username);return "main";}else {//如果失效,重新登录String url = getFullUrl(request);return "redirect:"+String.format(USER_CENTER_LOGIN_URL, URLEncoder.encode(url, "UTF-8"));}}else {//从参数中取,如果从用户中心跳转回来,会在参数中传递tokentk = getFromParam(request);if(tk != null) {//还是要校验下参数的有效性String username = getFromUserCenter(tk);if(username != null) {//生成自己站点下面的cookieCookie cookie = new Cookie(COOKIE_NAME, tk);cookie.setMaxAge(Integer.MAX_VALUE);response.addCookie(cookie);//进入主页model.addAttribute("username", username);return "main";}else {//说明token已经过期,进入用户中心做登录String url = getFullUrl(request);return "redirect:"+String.format(USER_CENTER_LOGIN_URL, URLEncoder.encode(url, "UTF-8"));}}else {//进入用户中心做登录String url = getFullUrl(request);return "redirect:"+String.format(USER_CENTER_LOGIN_URL, URLEncoder.encode(url, "UTF-8"));}}}
usercenter处理业务域的跳转和做登录:
当各个站定向用户中跳的时候,首先访问这个USER_CENTER_LOGIN_URL:
@GetMapping(value="/login")public String to_login(HttpServletRequest request, Model model)throws Exception {String redir_url = request.getParameter("redir_url");//先看是否存在usercenter下面额cookie//只要有一个站点登录成功过,其他的站点往用户中心跳的时候//都会携带这个cookie,只要有效,就可以不用再次登陆String tk = getFromCookie(request);if(tk == null) {//去登陆model.addAttribute("redir_url", redir_url);return "login";}else {//check是否有效String username = getByToken(tk);if(username == null) {model.addAttribute("redir_url", redir_url);return "login";}else {//如果有效,直接跳回去redir_url = addTokenToUrl(redir_url, tk);return "redirect:"+redir_url;}}}
在用户中心做登录:
@PostMapping(value="/login")public String do_login(LoginUser loginUser, String redirect_url, HttpServletResponse res, Model model)throws Exception {String username = loginUser.getUsername();LoginUser userDB = getFromDB(username);if(userDB == null) {model.addAttribute("errmsg", "用户不存在");model.addAttribute("redir_url", redirect_url);return "login";}String pwdDB = userDB.getPassword();if(!pwdDB.equals(loginUser.getPassword())) {model.addAttribute("errmsg", "密码错误");model.addAttribute("redir_url", redirect_url);return "login";}//存redis,注意要设置有效期String tk = UUID.randomUUID().toString();redis.put(tk, userDB);//生成用户中心的cookieCookie cookie = new Cookie(COOKIE_NAME, tk);cookie.setDomain("www.usercenter.com");cookie.setPath("/");cookie.setMaxAge(Integer.MAX_VALUE);res.addCookie(cookie);//跳转回去redirect_url = addTokenToUrl(redirect_url, tk);return "redirect:"+redirect_url;}
下面是用户中心校验token是否有效,有效就返回用户信息:
@GetMapping(value="/getByToken")@ResponseBodypublic String getByToken(String token)throws Exception {//这里注意:延长下redis的有效期LoginUser user = redis.get(token);if(user != null) {return user.getUsername();}else {return null;}}
代码其实还是挺简单的,主要的界面截图如下:
(1)浏览器访问http://www.site1.com/index.html,这个是个静态页面,不需要登陆:
this is site1 index<br/>
<input type="button" onclick="visitMain()" value="visit main page" />
<script>
function visitMain(){window.location.href="http://www.site1.com/site1/main";
}
</script>
(2)点击visit main page按钮,访问site1站点下需要登录的页面,此时会重定向到usercenter,完整的url是
http://www.usercenter.com/user_center/login?redir_url=http%3A%2F%2Fwww.site1.com%2Fsite1%2Fmain:
(3)输入用户名和密码做登陆,登录成功,跳转回site1,完整的url:
http://www.site1.com/site1/main?user_center_tk=19d89eb0-b135-492f-a684-2a8fc6f7e859,注意:参数中传递了token:
此时,刷新页面的的话,site1是可以拿到cookie中的token的,只需要去检验token有效性即可。
欢迎你:<span th:text="${username}"></span>,这是site1的主页面<br/>
<input type="button" onclick="goToSite2()" value="go to site2"/>
<script>
function goToSite2(){window.location.href="http://www.site2.com/site2/main";
}
</script>
(4)点击页面上go to site2按钮,会直接进入site2的主界面而不需要做登录,因为site2首先是去用户中心,用户中心会拿到之前设置的cookie,校验通过以后,会跳转回site2,注意看site2的url:
http://www.site2.com/site2/main?user_center_tk=19d89eb0-b135-492f-a684-2a8fc6f7e859:
<span th:text="${username}"></span>,这是site2的主页面<br/>
<input type="button" onclick="goToSite1()" value="go to site1"/>
<script>
function goToSite1(){window.location.href="http://www.site1.com/site1/main";
}
</script>
以上就是整个项目的实现,主要的点在于cookie不能跨域,A站只能设置A站的cookie,也只能读取A站的cookie,明白了这个就ok了。退出的代码就不写了,在site1退出的时候,首先要删除site1的cookie,然后要删除用户中心的redis即可。大家有兴趣可以自己实现下。
完整的代码下载:扫码关注文章开头的公众号看原文
代码仅仅是为了展示整个流程,如果是在实际项目中使用可以在业务域上用拦截器之类的来做。
单点登录系统SSO是如何实现的?相关推荐
- java sso单点登录源码_Java单点登录系统 sso源码下载
这是一个使用Java开发的单点登陆系统(sso). 运行截图 单点登陆介绍 单点登录,这就是我们通常称之为SSO.一般来说,大型系统平台将使用这些东西.它解决了频繁登录和验证的过程,即用户的一次登录被 ...
- 单点登录系统(SSO)和Session共享解释
在企业发展初期,企业使用的系统很少,通常一个或者两个,每个系统都有自己的登录模块,运营人员每天用自己的账号登录,很方便. 但随着企业的发展,用到的系统随之增多,运营人员在操作不同的系统时,需要多次登录 ...
- 单点登录系统(SSO)的开发思路
单点登录系统的类别: 就目前比较流行的应用来看,单点登录系统主要分为三种类型:一种是基于oauth协议的网络令牌(我是这么叫的),一种是基于Web Service或者简单Http协议实现的 ...
- 单点登录系统SSO概述 | 单点登录讲解(1)
本项目主要讲解的是单点登录系统的原理及其实现. 本章主要讲解的是单点登录系统的概述部分. 单点登录 单点登录顾名思义就是从一个系统进行登录操作,就可以访问其他附近的系统.单点登录避免了用户重复的登录过 ...
- 单点登录系统(SSO)详细设计说明书
1.引言 1.1编写目的 为了单点登录系统(SSO系统)的可行性,完整性,并能按照预期的设想实现该系统,特编写需求说明书. 同时,说明书也发挥与策划和设计人员更好地沟通的作用. 1.2背景 a.鉴于集 ...
- 09-微服务版单点登陆系统(SSO)实践
目录 单点登陆系统简介 背景分析 单点登陆系统概述 单点登陆系统解决方案设计 单点登陆系统初步设计 服务设计 工程结构设计 SSO父工程创建及初始化 创建父工程 父工程pom文件初始配置 系统基础服务 ...
- 单点登录系统(流程简介)
一.概述 单点登录系统SSO(Single Sign On)是在多个应用系统中,用户只需要登录一次就可以访问相互信任的其它系统 二.系统简介 流程: 1.用户访问应用一 2.应用一检查用户登录,如果用 ...
- codeigniter 禁止ip登录_「开源资讯」baigo SSO v4.0 beta-3 发布,单点登录系统
来源:https://www.oschina.net/news/117020/baigo-sso-4-beta3-released 简介 baigo SSO 是一款基于 HTTP 协议的单点登录系统, ...
- SpringBoot+MyBatis+Redis实现SSO单点登录系统(二)
SpringBoot+MyBatis+Redis实现SSO单点登录系统(二) 三.代码 配置文件配置数据库,redis等相关的信息. # See http://docs.spring.io/sprin ...
最新文章
- PHP配置问题:AppServ安装discuz出错 Fatal error:
- 正则表达式从基础到深入实战
- 2020 China Collegiate Programming Contest Weihai Site补题部分
- wpf 创建附加属性实例
- jQuery UI dialog插件出错信息:$(this).dialog is not a function
- iframe导致的IE6下https页面安全提示
- ElasticStack系列之九 master、data 和 client 节点
- java中switch、while、do...while、for
- 怎么删除计算机中的服务,小白教你怎么删除系统服务
- CentOS 开启端口
- PHP操作MongoDB技術總結
- QuantumultX 初学者傻瓜教程
- 在厉害的圈子里耳濡目染 No.110
- 宠物商店mysql数据库设计_宠物商城数据库设计
- mysql创建/编辑表时的 ROW_FORMAT = Dynamic 和 Compact 有什么区别
- 力扣(392.521)补8.26
- 买眼镜踩坑【吐槽一下实体店的坑】
- 私域流量有什么特点?
- Linux 私房菜速读
- 英语这样学最有效------少走弯路的学习方法