聊聊我在这家公司设计的SSO

最近小明遇到一个需求:需要将几个独立的系统(子系统)汇总到一个集中的系统(父系统)当中,当用户在父系统登录过后,再点击这几个子系统,就可以免登录跳转到任意一个系统。当时一听,duang~duang~就有很多方案涌进来(吹牛的),但只有下面这个方案得到了leader的肯定,如今已经在线上跑着了,接下来给大家复盘一下。

看完这个需求,大家是不是第一感觉就是:这不就是SSO(单点登录)系统嘛?

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一退出(single sign-off)就是指,只需要单一的退出动作,就可以结束对于多个系统的访问权限。

是的,没错,小明接到这个需求以后,整体思路也是按着SSO设想的,但是细想之后,发现不能完全照搬,要考虑项目的实际情况:比如已知的几个子系统是之前的已经开发好的,不能大动干戈,需要平滑接入父系统,而且根据需求,SSO的功能也没必要全部实现,简而言之,就是一个阉割版的SSO。

小明只需要实现:用户在父系统账号密码登录后,通过点击任意一个子系统的功能按钮(不需要重复输入账号登录)能够跳转子系统功能页即可。

设计流程

项目

一个简单朴素的SpringBoot项目

时序图

说干就干,用户输入账号密码,请求SSO用户登录模块进行账号密码校验,校验通过后建立全局会话,并且返回前端token凭证(我使用的是sessionId),跳转其他系统时携带token,其他系统拿到token后,再调用SSO平台进行token校验,如果校验通过,则用户可在子系统内建立会话,用户跳转系统完成。下面给大家举例SSO跳转一个子系统的时序图:

简易sso流程图

在这里插一嘴哈,我使用的流程图工具是ProcessOn,是一款在线画图工具,非常适合画各种示意图,体验极佳,如果大家想尝试一下,可以使用我的邀请链接注册使用~

整个流程图如上面所示,下面主要针对各个功能点进行详细说明。

SSO系统的登录与会话保持

本次会话管理采用的是redis session,spring完美支持redis存储session信息,此外还支持MONGODB\JDBC\HAZELCAST等存储会话方式。通过redis存储session,可以满足集群部署、分布式系统的session共享(当然这些都是后话)。

pom.xml依赖配置如下

        <!--redis 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--sessions 依赖-->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

application.yml配置redis及session

spring:
  redis:
    host: 127.0.0.1
    password: 123456
    port: 6379
    timeout: 1500
    database: 0
    jedis:
      pool:
        max-active: 1000
        max-wait: -1
        max-idle: 10
        min-idle: 5
  # 设置session存储类型为redis 
  session:
    store-type: redis

此时,redis存储会话配置已经完成,但总觉得缺少什么,嗷,原来除此之外,我们还需要设置session的有效时长,application.yml中的配置如下:

server:
  servlet:
    session:
        # 支持Duration表达式,此时表示120分钟
      timeout: PT120M

当然,我们还需要在Springboot主方法通过@EnableRedisHttpSession开启redis session

注意:如果上面配置的session有效时长不生效,我们可以在注解属性上配置session有效时间(不瞒大家,我就是在此处配置才生效的)

@SpringBootApplication
// 开启redis session ,并且设置session有效时长
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 7200)
public class XiaoMingApplication {

    public static void main(String[] args) {
        SpringApplication.run(XiaoMingApplication.class, args);
    }
}

至此,sso系统的登录及会话管理就完成啦。接下来看一下与其他系统如何交互。

跳转子系统

流程

如果SSO已经登录 -> 用户点击某个子系统按钮(和负责A系统的人员约定好的链接)发起get请求 -> A系统后端接收到请求 -> 调用SSO系统进行token校验(下面会讲到) -> 建立会话,例如

http://xxx/jump?token=123456

这是一个在地址栏输入的get请求,该接口需要特殊处理,后端拦截器需要放行。

参数说明

参数

说明

xxx

此处xxx为域名,jump为系统A提供的接口url地址,可以自定义,需要约定告知

token

返回的校验凭证

该接口其实就需要干两件事情:

  1. 请求SSO进行token校验(类似之前的用户、密码登录,只不过调用SSO平台接口校验);
  2. 建立本地会话(和账号密码登录成功之后的建立会话方式一致)。
  3. 控制校验通过后的页面跳转。

SSO为子系统提供token校验接口

上面讲到SSO会暴漏一个token校验接口,这一块逻辑很简单,就是拿着token去redis中查找对应的用户信息是否存在。众所周知,小明是一个懒人,为了投机取巧,小明token的生成规则,就是session的id,因此,判断用户是否登录,其实就是根据sessionId查找redis是否存在会话,此处有亮点。通过查看源码,我发现这个功能,根本不用我们自己去实现,spring已经想到我们会用到。

spring提供了一个接口org.springframework.session.SessionRepository

package org.springframework.session;

public interface SessionRepository<S extends Session> {
    S createSession();

    void save(S var1);
    // 这个就是
    S findById(String var1);

    void deleteById(String var1);
}

其中S findById(String var1)就是我们要调用的方法,这个方法作用就是根据sessionId去会话中心查找会话对象,正是我们所需要的,我们只需通过@Autowired获取,开箱即用~我们一起看一下业务代码:

@Autowired
private SessionRepository sessionRepository;
    @PostMapping("/checkToken")
public BaseResponseFacade checkToken(@RequestBody UserLoginVo userLoginVo) {
    if (Objects.isNull(userLoginVo)) {
        return ResponseUtil.error(NEED_LOGIN);
    }
    String token = userLoginVo.getToken();
    Session session = sessionRepository.findById(token);
    if (Objects.isNull(session)) {
        return ResponseUtil.error(NEED_LOGIN);
    }
    AdverInfo adverInfo = JSON.parseObject(session.getAttribute("adverInfo"), AdverInfo.class);
    return ResponseUtil.success(adverInfo);
}

SSO和子系统的交互文档也贴出来给大家一睹为快

接口调用请求说明

  • 请求方式:POST
  • 请求格式:JSON
  • 请求地址
  • 测试:http:/xxx/checkToken
  • 正式:http://xxx/checkToken
  • POST数据示例
{
    "token": "123456"
}
参数说明

参数

说明

xxx/checkToken

xxx为域名,checkToken为营销云平台提供的校验接口地址

token

调用接口凭据

返回说明

正常时返回的json数据包示例

如果SSO校验通过,则系统A可以与与当前用户建立本地会话,用户正常进入系统

{
    "data":{
    "XXX":"XXX"
    },
    "errorMsg":"成功",
    "errorCode":0
}

参数说明

参数

说明

errorCode

0:表示请求成功

errorMsg

返回码说明

XXX

其他相关信息

异常时返回的json数据包示例

当SSO后台校验失败时返回参数如下

{
  "errorMsg": "NEED_LOGIN",
  "errorCode": 10
}

参数说明

参数

说明

errorMsg

错误信息说明

errorCode

错误标志

错误码说明

错误码

说明

1

系统繁忙

10

需要用户登录

500

服务器内部错误

小明设计的简洁版sso就大抵如此,大家可以作为一个sso入门demo来看待?,如果大家有什么建议或问题,欢迎留言~

本文分享自微信公众号 - 程序员小明(coderxinqiji)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据和云

史上最通俗分布式锁解读

首先,分布式锁和我们平常讲到的锁原理基本一样,目的就是确保在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法、变量。

8820
来自专栏汇智网教程

交易所对接以太坊钱包服务设计与实现

交易所钱包服务是加密货币交易所系统中的重要组成部分,它负责与各种不同的区块链的交互,实现用户地址生成、充值与提现等功能。本文以对接以太坊区块链的钱包服务为例,介...

42910
来自专栏每天进步一点点

think-queue 解析上

分析之前请大家务必了解消息队列的实现 如果不了解请先阅读下: 有赞消息队列设计 去哪儿网消息队列设计

13620
来自专栏Java架构沉思录

想不到吧?我是这样用Redis实现消息定时推送的!

先说一下领劵中心的项目吧,这个项目就类似京东app的领劵中心,当然图是截取京东的,公司的就不截了。。。

20510
来自专栏Tech爬虫(公众号php_pachong)

app后台技术

首先应用程序框架,随你自己业务需求而定,可以选择SSH或者SSM,或者看自己业务量大小。

12920
来自专栏程序员开发者社区

JedisPool连接池

172.31.1.135:7002> CONFIG GET maxclients ...

11730
来自专栏程序员开发者社区

Redis 分布式锁应用

Redis 最常使用的场景是作为缓存,缓存用户信息,会话信息,还有一些热点信息。

14810
来自专栏Linyb极客之路

Redis项目实战,一些经验总结

不管是简单和复杂的数据都可以直接转为string存储。推荐阅读:Redis 的 8 大应用场景。

14910
来自专栏云服务器999+

最高50万QPS,腾讯云新发布Redis 4.0标准版突破性能极限

Redis在缓存应用场景中拥有不可取代的地位,被广泛应用于数据缓存、游戏存储、分布式会话存储、实时分析和机器学习等场景。腾讯云在Redis数据库领域的不断突破,...

12540
来自专栏架构师

用NOSql给高并发系统加速

随着互联网大潮的到来,越来越多网站,应用系统需要海量数据的支撑,高并发、低延迟、高可用、高扩展等要求在传统的关系型数据库中已经得不到满足,或者说关系型数据库应对...

11620

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励