前言

在早期,项目规模不大的时候,企业中存在的系统不多,通常为1或者2个。每个系统都有自己独立的登陆模块,这样用户进行登陆也不是特别麻烦,分别进行登陆就可以了。但是随着企业规模不断变大,随之而然的系统模块也越来越多,而每个模块都有自己独立的登陆,那么用户就会有很多登陆账号,想要进入系统都得进行单独登陆,这不然是最痛苦的。
    那么能不能只在一个系统进行登陆成功之后,在其他系统就不用再登陆了呢?就行我在淘宝网站进行登陆之后,点击链接进入天猫商城一样,不用再重新进行登陆。解决办法是有的,那就是使用单点登陆方案解决。方案有了,不同实现的技术就会出现。下面介绍的是许雪里老师编写开源出来的xxl-sso框架。
    单点登陆英文全称Single Sign On ,简称就是SSO。它的解释就是:在多个应用系统中,只需要在一个系统中完成登陆,就可以访问其他相互信任的应用系统。
    之前是每一个系统都有单独的登陆认证模块,现在是将登陆功能统一的由SSO进行登陆认证,其它的应用模块没有登陆功能。

Xxl-sso是一个分布式单点登陆框架。只需要登陆一次就可以访问所有相互信任的应用系统。下面先介绍一下它的特性有哪些:

简洁:API直观简洁,可快速上手
轻量级:环境依赖小,部署与接入成本较低
单点登录:只需要登录一次就可以访问所有相互信任的应用系统
分布式:接入SSO认证中心的应用,支持分布式部署
HA:Server端与Client端,均支持集群部署,提高系统可用性
跨域:支持跨域应用接入SSO认证中心
Cookie+Token均支持:支持基于Cookie和基于Token两种接入方式,并均提供Sample项目
Web+APP均支持:支持Web和APP接入
实时性:系统登陆、注销状态,全部Server与Client端实时共享
CS结构:基于CS结构,包括Server”认证中心”与Client”受保护应用”
记住密码:未记住密码时,关闭浏览器则登录态失效;记住密码时,支持登录态自动延期,在自定义延期时间的基础上,原则上可以无限延期
路径排除:支持自定义多个排除路径,支持Ant表达式,用于排除SSO客户端不需要过滤的路径

xxl-sso框架结构

项目下载地址: https://github.com/xuxueli/xxl-sso

  • 包含三个模块

xxl-sso-server:中央认证服务,支持集群
xxl-sso-core:Client端依赖,主要作用为路径的排除,哪些路径不需要登陆就可以访问的、登陆时token的认证检查等
xxl-sso-samples:单点登陆Client端接入示例项目xxl-sso-web-sample-springboot:基于Cookie接入方式,供用户浏览器访问,springboot版本xxl-sso-token-sample-springboot:基于Token接入方式,常用于无法使用Cookie的场景使用,如APP、Cookie被禁用等,springboot版本
  • 环境
    JDK:1.7+
    Redis:4.0+
  • 架构图
  • 相关配置文件介绍
  1. xxl-sso-server认证中心(SSO Server)
    配置文件位置:application.properties
### xxl-sso
redis 地址: 如 "{ip}"、"{ip}:{port}"、"{redis/rediss}://xxl-sso:{password}@{ip}:{port:6379}/{db}";多地址逗号分隔
xxl.sso.redis.address=redis://127.0.0.1:6379
登录态有效期窗口,默认24H,当登录态有效期窗口过半时,自动顺延一个周期
xxl.sso.redis.expire.minite=1440

密码配置:可查看com.xxl.sso.core.util下的JedisUtil类中获取ShardedJedis实例代码

2. xxl-sso-web-sample-springboot(应用client)
引入xxl-sso-core核心依赖包

<dependency><groupId>com.xuxueli</groupId><artifactId>xxl-sso-core</artifactId><version>${最新稳定版}</version>
</dependency>

配置xxlSsoFilter

配置文件位置:application.properties

### xxl-sso
### Sso server认证中心地址
xxl.sso.server=http://xxlssoserver.com:8080/xxl-sso-server
### 注销登陆path
xxl.sso.logout.path=/logout
### 路径排除path,允许设置多个,且支持Ant表达式。用于排除SSO客户端不需要过滤的路径
xxl-sso.excluded.paths=
// redis address, like "{ip}"、"{ip}:{port}"、"{redis/rediss}://xxl-sso:{password}@{ip}:{port:6379}/{db}";Multiple "," separated
xxl.sso.redis.address=redis://127.0.0.1:6379
  • 修改hosts文件,模拟真实环境
127.0.0.1 xxlssoserver.com
127.0.0.1 xxlssoclient1.com
127.0.0.1 xxlssoclient2.com

如果在hosts文件中添加了以上内容,不生效,解决方法:
打开命令行窗口:
    ipconfig /displaydns 查看配置的dns是否存在
    ipconfig /flushdns刷新dns配置

  • 项目启动
    分别运行“xxl-sso-server”“xxl-sso-web-sample-springboot”项目

相应访问测试地址

1、SSO认证中心地址:
http://xxlssoserver.com:8080/xxl-sso-server
2、Client1应用地址:
http://xxlssoclient1.com:8081/xxl-sso-token-sample-springboot/
3、Client2应用地址:
http://xxlssoclient2.com:8081/xxl-sso-token-sample-springboot/
  • 源码跟踪
  1. 访问Client1应用地址,会经过XxlSsoWebFilter拦截。XxlSsoWebFilter存在于核心依赖包中,应用中配置如下:
  2. 在XxlSsoWebFilter过滤器中,先进行init初始化操作,初始化内容为在Client1应用中配置的相关信息。然后走doFilter方法,实现登陆检查。
  3. doFilter方法具体代码如下:
    @Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse res = (HttpServletResponse) response;// (1) 请求路径String servletPath = req.getServletPath();//(2) 路径排除if (excludedPaths!=null && excludedPaths.trim().length()>0) {for (String excludedPath:excludedPaths.split(",")) {String uriPattern = excludedPath.trim();// 支持ANT表达式if (antPathMatcher.match(uriPattern, servletPath)) {chain.doFilter(request, response);return;}}}// (3) 登出if (logoutPath!=null&& logoutPath.trim().length()>0&& logoutPath.equals(servletPath)) {// remove cookieSsoWebLoginHelper.removeSessionIdByCookie(req, res);// redirect logoutString logoutPageUrl = ssoServer.concat(Conf.SSO_LOGOUT);res.sendRedirect(logoutPageUrl);return;}// (4) 登陆检查XxlSsoUser xxlUser = SsoWebLoginHelper.loginCheck(req, res);// (5) 用户信息不存在时if (xxlUser == null) {String header = req.getHeader("content-type");boolean isJson=  header!=null && header.contains("json");if (isJson) {//(6) json消息res.setContentType("application/json;charset=utf-8");res.getWriter().println("{\"code\":"+Conf.SSO_LOGIN_FAIL_RESULT.getCode()+", \"msg\":\""+ Conf.SSO_LOGIN_FAIL_RESULT.getMsg() +"\"}");return;} else {// (7) 访问源地址 http://xxlssoclient1.com:8081/xxl-sso-web-sample-springboot/String link = req.getRequestURL().toString();// (8) 重定向地址 http://xxlssoserver.com:8080/xxl-sso-server/login?redirect_url=http://xxlssoclient1.com:8081/xxl-sso-web-sample-springboot/String loginPageUrl = ssoServer.concat(Conf.SSO_LOGIN)+ "?" + Conf.REDIRECT_URL + "=" + link;res.sendRedirect(loginPageUrl);return;}}// ser sso userrequest.setAttribute(Conf.SSO_USER, xxlUser);// (9) 过滤器放行chain.doFilter(request, response);return;}

编号注释(1-9):
(1)-(3):主要做路径排除和用户登出的操作。
(4):登陆用户信息检查(重点部分),下面会进行代码跟踪。
(5):如果没有用户信息的处理。
(6-9):相关信息注释,下面会使用到。

  1. 接下来,我们看些编号(4)做的具体处理逻辑
    调用SsoWebLoginHelper中的loginCheck方法

    又会调用SsoTokenLoginHelper中的lgoinCheck方法,代码如下:
public static XxlSsoUser loginCheck(String  sessionId){// 解析key sessionId生成规则 userId+"_"+versionString storeKey = SsoSessionIdHelper.parseStoreKey(sessionId);if (storeKey == null) {return null;}//根据key从redis中获取用户信息XxlSsoUser xxlUser = SsoLoginStore.get(storeKey);if (xxlUser != null) {String version = SsoSessionIdHelper.parseVersion(sessionId);if (xxlUser.getVersion().equals(version)) {// 判断时间是否过半,如果是 自动刷新if ((System.currentTimeMillis() - xxlUser.getExpireFreshTime()) > xxlUser.getExpireMinite()/2) {xxlUser.setExpireFreshTime(System.currentTimeMillis());SsoLoginStore.put(storeKey, xxlUser);}return xxlUser;}}return null;}/*** login check** @param request* @return*/public static XxlSsoUser loginCheck(HttpServletRequest request){String headerSessionId = request.getHeader(Conf.SSO_SESSIONID);return loginCheck(headerSessionId);}
  1. 如果(4)走完,XxlSsoUser为空

    判断是否是json请求,此时请求不是json的。重定向到(8)SSO Server项目进行认证:
    认证地址:http://xxlssoserver.com:8080/xxl-sso-server/login?redirect_url=http://xxlssoclient1:8081/xxl-sso-web-sample-springboot/

    将重定向的地址存放到登陆隐藏域中
  2. 登陆
    点击登陆,请求/doLogin
    @RequestMapping("/doLogin")public String doLogin(HttpServletRequest request,HttpServletResponse response,RedirectAttributes redirectAttributes,String username,String password,String ifRemember) {//判断是否勾选了记住我的功能boolean ifRem = (ifRemember!=null&&"on".equals(ifRemember))?true:false;// 查询用户逻辑ReturnT<UserInfo> result = userService.findUser(username, password);if (result.getCode() != ReturnT.SUCCESS_CODE) {redirectAttributes.addAttribute("errorMsg", result.getMsg());redirectAttributes.addAttribute(Conf.REDIRECT_URL, request.getParameter(Conf.REDIRECT_URL));return "redirect:/login";}// 模拟用户信息XxlSsoUser xxlUser = new XxlSsoUser();xxlUser.setUserid(String.valueOf(result.getData().getUserid()));xxlUser.setUsername(result.getData().getUsername());xxlUser.setVersion(UUID.randomUUID().toString().replaceAll("-", ""));xxlUser.setExpireMinite(SsoLoginStore.getRedisExpireMinite());xxlUser.setExpireFreshTime(System.currentTimeMillis());// 生成sessionId,生成规则:userId+"_"+versionString sessionId = SsoSessionIdHelper.makeSessionId(xxlUser);// 存储到redis和cookie中SsoWebLoginHelper.login(response, sessionId, xxlUser, ifRem);// 获取隐藏域中存放的登陆成功之后的地址String redirectUrl = request.getParameter(Conf.REDIRECT_URL);if (redirectUrl!=null && redirectUrl.trim().length()>0) {String redirectUrlFinal = redirectUrl + "?" + Conf.SSO_SESSIONID + "=" + sessionId;return "redirect:" + redirectUrlFinal;} else {return "redirect:/";}}

携带sessionId重定向到源请求资源地址:http://xxlssoclient1.com:8081/xxl-sso-web-sample-springboot/,此时又会经过XxlSsoWebFilter过滤器
走doFilter方法

调用loginCheck方法检查验证,并写入cookie


到这里xxlssoclient1.com登陆成功

  • 请求Client2应用地址
    同样会经过XxlSsoWebFilter,调用SsoWebLoginHelper.loginCheck方法校验用户信息。最后会被重定向到Sso Server
    此时SSO Server怎么知道client2不用在进行登陆了呢?看SSO Server项目中代码
 @RequestMapping(Conf.SSO_LOGIN)public String login(Model model, HttpServletRequest request, HttpServletResponse response) {// 做登陆检查,此时获取cookieSessionid是从xxlssoserver.com域名下获取的,一定存在用户信息,此前在client1系统中已经登陆过了。XxlSsoUser xxlUser = SsoWebLoginHelper.loginCheck(request, response);if (xxlUser != null) {// success redirectString redirectUrl = request.getParameter(Conf.REDIRECT_URL);if (redirectUrl!=null && redirectUrl.trim().length()>0) {String sessionId = SsoWebLoginHelper.getSessionIdByCookie(request);String redirectUrlFinal = redirectUrl + "?" + Conf.SSO_SESSIONID + "=" + sessionId;;// 重定向到client2应用地址,携带sessionIdreturn "redirect:" + redirectUrlFinal;} else {return "redirect:/";}}model.addAttribute("errorMsg", request.getParameter("errorMsg"));model.addAttribute(Conf.REDIRECT_URL, request.getParameter(Conf.REDIRECT_URL));return "login";}

同client1重定向方式相同,会经过XxlSsoWebFilter过滤器
走doFilter方法

调用loginCheck方法检查验证,并写入cookie

至此实现了在Client1系统中完成登陆后,访问Client2系统,不再进行登陆操作。

sso单点登陆实现过程汇总记录相关推荐

  1. IM开发基础知识补课(一):正确理解前置HTTP SSO单点登陆接口的原理

    1.前言 一个安全的信息系统,合法身份检查是必须环节.尤其IM这种以"人"为中心的社交体系,身份认证更是必不可少. 一些PC时代小型IM系统中,身份认证可能直接做到长连接中(也就是 ...

  2. IM开发基础知识补课:正确理解前置HTTP SSO单点登陆接口的原理

    1.前言 一个安全的信息系统,合法身份检查是必须环节.尤其IM这种以"人"为中心的社交体系,身份认证更是必不可少. 一些PC时代小型IM系统中,身份认证可能直接做到长连接中(也就是 ...

  3. 京东SSO单点登陆实现分析

    京东的sso流程: 初始访问状态: cookies: http请求: 1.在首页点击登陆,跳转至passport.360buy.com,给予验证cookie alc(可以试试在提交登陆信息前删除该co ...

  4. 企业内部应用(阿里商旅)集成 钉钉 与 微信 实现SSO单点登陆

    1.钉钉准备工作 (1) 创建应用 https://open-dev.dingtalk.com/fe/app#/corp/app (2)获取应用凭证 记录 AgentId AppKey AppSecr ...

  5. Lind.DDD.SSO单点登陆组件的使用(原创)

    回到目录 一般sso的说明 在Lind.DDD框架里,有对单点登陆的集成,原理就是各个网站去sso网站统一登陆授权,之后在sso网站将登陆的token进行存储,存储方式随你(cache,redis,m ...

  6. 开源sso单点登陆系统推荐

    一.CAS   地址https://www.apereo.org/projects/cas CAS是Central Authentication Service的缩写,中央认证服务,一种独立开放指令协 ...

  7. cas sso单点登录 登录过程和登出过程原理说明

    CAS大体原理我就不说了,网上一大把,不过具体交互流程没说清楚,所以有这篇文章,如果有错误,请多多指教 登录过程 用户第一次访问一个CAS 服务的客户web 应用时(访问URL :http://192 ...

  8. 带你快速了解SSO单点登陆

    引入 定义:在多个应用系统中共享登录.撤销状态 这里说的多个应用系统其实指的不同的域 同源(同一个域):域名.端口.协议均相同 举例:淘宝(www.taobao.com)和天猫(www.tmall.c ...

  9. 简单入门SSO单点登陆——认证服务

    SSO在我们的应用中非常常见,例如我们在OA系统登录了,我们就可以直接进入采购系统,不需要再登录了,这样使我们非常方便.现在网上也有很多实现方法,于是乎我也想写一个看看.我主要用到的是cookie的机 ...

最新文章

  1. html随机播放不同的音乐,如何随机播不同的背景音乐
  2. Bootstrap File Input 真正 解决跨域问题
  3. 中考计算机应用技术有前途吗,被中考“淘汰”的职高生,未来的就业前景怎么样,你知道吗?...
  4. [zz]正则表达式使用详解
  5. linux安装comfast网卡驱动,电脑如何通过usb共享手机网络 Linux安装wifi 无线网络 811AC usb网卡驱动...
  6. 高并发,分布式系统要点
  7. 数据结构与算法 - 递归回溯(迷宫问题)
  8. 用TextPaint来绘制文字
  9. faster rcnn resnet_Faster-rcnn 代码详解
  10. 封装一个方法,找出数组中重复数大于n的元素集合
  11. oracle timestamp计算两分钟前_阿里数据库真的超过Oracle了么?
  12. SQL Server 2008 R2下载地址
  13. 如何获取具有日期,小时和分钟的ISO 8601格式的当前时刻?
  14. 什么是多径效应?多径效应怎么消除?多径效应解决方法
  15. 学习版origin申请安装
  16. 几个著名的心理学实验
  17. HDU多校第三场6608 Fansblog(米勒罗宾+威尔逊定理)
  18. 利用ChatGPT学习生物信息数据分析
  19. 手写数字识别实现课设cnsd博客_讯飞输入法Android V9.1.9465 重磅升级拼音手写A.I.引擎...
  20. .idea文件夹如何取消版本控制

热门文章

  1. 外呼系统的四大技术原理,电销外呼系统常识
  2. 如何删除计算机自动更新补丁,删除自动更新中的WIN10升级补丁
  3. MATLAB产生随机阶跃信号
  4. C#实现电脑桌面端的本地txt电子书阅读器
  5. 电商实训四:网络营销方案设计及实施
  6. Matlab求矩阵的Jordan标准形
  7. 解决spa白屏方法之ssr及ssr优化
  8. iOS App瘦身---清理iOS工程里无用的图片
  9. 十进制转二进制(C++)
  10. 美国海军战列舰——田纳西号(Tennessee)