如何设计一套单点登录系统(上)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 昨天介绍了API接口设计token鉴权方案,其实token鉴权最佳的实践场景就是在单点登录系统上。在企业发展初期,使用的后台管理系统还比较少,一个或者两个。以电商系统为例,在起步阶段,可能只有一个商城下单系统和一个后端管理产品和库存的系统。

一、介绍

昨天介绍了API接口设计token鉴权方案,其实token鉴权最佳的实践场景就是在单点登录系统上。

在企业发展初期,使用的后台管理系统还比较少,一个或者两个。

以电商系统为例,在起步阶段,可能只有一个商城下单系统和一个后端管理产品和库存的系统。

随着业务量越来越大,此时的业务系统会越来越复杂,项目会划分成多个组,每个组负责各自的领域,例如:A组负责商城系统的开发,B组负责支付系统的开发,C组负责库存系统的开发,D组负责物流跟踪系统的开发,E组负责每日业绩报表统计的开发...等等。

规模变大的同时,人员也会逐渐的增多,以研发部来说,大致的人员就有这么几大类:研发人员、测试人员、运维人员、产品经理、技术支持等等。

他们会频繁的登录各自的后端业务系统,然后进行办公。

此时,我们可以设想一下,如果每个组都自己开发一套后端管理系统的登录,假如有10个这样的系统,同时一个新入职的同事需要每个系统都给他开放一个权限,那么我们可能需要给他开通10个账号。

随着业务规模的扩大,大点的公司,可能高达一百多个业务系统,那岂不是要配置一百多个账号,让人去做这种操作,岂不伤天害理。

面对这种繁琐而且又无效的工作,IT大佬们想到一个办法,那就是开发一套登录系统,所有的业务系统都认可这套登录系统,那么就可以实现只需要登录一次,就可以访问其他相互信任的应用系统。

这个登录系统,我们把它称为:单点登录系统。

好了,言归正传,下面我们从两个方面来介绍单点登录系统的实现。

  • 方案设计
  • 项目实践

二、方案设计

2.1、单体后端系统登录

在传统的单体后端系统中,简单点的操作,我们一般都会这么玩,用户使用账号、密码登录之后,服务器会给当前用户创建一个session会话,同时也会生成一个cookie,最后返回给前端。

2.jpg

当用户访问其他后端的服务时,我们只需要检查一下当前用户的session是否有效,如果无效,就再次跳转到登录页面;如果有效,就进入业务处理流程。

但是,如果访问不同的域名系统时,这个cookie是无效的,因此不能跨系统访问,同时也不支持集群环境的共享。

对于单点登录的场景,我们需要重新设计一套新的方案。

2.2、单点登录系统登录

先来一张图!

3.jpg

这个流程图,就是单点登录系统与应用系统之间的交互图。

当用户登录某应用系统时,应用系统会把将客户端传入的token,调用单点登录系统验证token合法性接口,如果不合法就会跳转到单点登录系统的登录页面;如果合法,就直接进入首页。

进入登录页面之后,会让用户输入用户名、密码进行登录验证,如果验证成功之后,会返回一个有效的token,然后客户端会根据服务端返回的参数链接,跳转回之前要访问的应用系统。

接着,应用系统会再次验证token的合法性,如果合法,就进入首页,流程结束。

引入单点登录系统后,接入的应用系统不需要关系用户登录这块,只需要对客户端的token做一下合法性鉴权操作就可以了。

而单点登录系统,只需要做好用户的登录流程和鉴权并返回安全的token给客户端。

有的项目,会将生成的token,存放在客户端的cookie中,这样做的目的,就是避免每次调用接口的时候都在url里面带上token。

但是,浏览器只允许同域名下的cookies可以共享,对于不同的域名系统, cookie 是无法共享的。

对于这种情况,我们可以先将 token 放入到url链接中,类似上面流程图中跳转思路,对于同一个应用系统,我们可以将token放入到 cookie 中,不同的应用系统,我们可以通过 url 链接进行传递,实现token的传输。

三、项目实践

在实践上,token的存储,有两种方案:

  • 存放在服务器,如果是分布式环境,一般都会存储在 redis 中
  • 存储在客户端,服务器做验证,天然支持分布式

3.1、存放在redis

存放在redis中,是一种比较常见的处理办法,最开始的时候也是这种处理办法。

当用户登录成功之后,会将用户的信息作为value,用uuid作为key,存储到redis中,各个服务集群共享用户信息。

代码实践也非常简单。

用户登录之后,将用户信息存在到redis,同时返回一个有效的token给客户端。

@RequestMapping(value = "/login", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
public TokenVO login(@RequestBody LoginDTO loginDTO){
    //...参数合法性验证
    //从数据库获取用户信息
    User dbUser = userService.selectByUserNo(loginDTO.getUserNo);
    //....用户、密码验证
    //创建token
    String token = UUID.randomUUID();
    //将token和用户信息存储到redis,并设置有效期2个小时
    redisUtil.save(token, dbUser, 2*60*60);
    //定义返回结果
    TokenVO result = new TokenVO();
    //封装token
    result.setToken(token);
    //封装应用系统访问地址
    result.setRedirectURL(loginDTO.getRedirectURL());
    return result;
}

客户端收到登录成功之后,根据参数组合进行跳转到对应的应用系统。

跳转示例如下:http://xxx.com/page.html?token=xxxxxx

各个应用系统,只需要编写一个过滤器TokenFilter对token参数进行验证拦截,即可实现对接,代码如下:

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException, SecurityException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    String requestUri = request.getRequestURI();
    String contextPath = request.getContextPath();
    String serviceName = request.getServerName();
    //添加到白名单的URL放行
    String[] excludeUrls = {
            "(?:/images/|/css/|/js/|/template/|/static/|/web/|/constant/).+$",
            "/user/login",
            "/user/createImage"
    };
    for (String url : excludeUrls) {
        if (requestUri.matches(contextPath + url) || (serviceName.matches(url))) {
            filterChain.doFilter(request, response);
            return;
        }
    }
    //运行跨域探测
    if(RequestMethod.OPTIONS.name().equals(request.getMethod())){
        filterChain.doFilter(request, response);
        return;
    }
    //检查token是否有效
    final String token = request.getHeader("token");
    if(StringUtils.isEmpty(token) || !redisUtil.exist(token)){
        ResultMsg<Object> resultMsg = new ResultMsg<>(4000, "token已失效");
        //封装跳转地址
        resultMsg.setRedirectURL("http://sso.xxx.com?redirectURL=" + request.getRequestURL());
        WebUtil.buildPrintWriter(response, resultMsg);
        return;
    }
    //将用户信息,存入request中,方便后续获取
    User user =  redisUtil.get(token);
    request.setAttribute("user", user);
    filterChain.doFilter(request, response);
    return;
}

上面返回的是json数据给前端,当然你还可以直接在服务器采用重定向进行跳转,具体根据自己的情况进行选择。

由于每个应用系统都可能需要进行对接,因此我们可以将上面的方法封装成一个公共jar包,应用系统只需要依赖包即可完成对接!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
27天前
|
NoSQL Redis
SSO单点登录核心原理
SSO单点登录核心原理
31 0
|
6月前
|
JavaScript 前端开发 PHP
什么是单点登录,单点登录的逻辑,思路,
什么是单点登录,单点登录的逻辑,思路,
|
2月前
|
存储 缓存
实现单点登录的方式
实现单点登录的方式
30 1
|
3月前
|
存储 JSON 安全
用户登录注册系统的安全性设计
用户登录注册系统的安全性设计
40 4
|
9月前
|
存储 前端开发 安全
强化用户体验与安全性:前端单点登录和统一认证的最佳实践与区别
互联网发展了这么多年,各种更新皆为了提供更好更安全的上网环境。同时为了提供更好的用户体验、减少用户反复输入用户名和密码的繁琐操作,并确保账户安全,前端领域中的单点登录(SSO)和统一认证(Unified Authentication)成为了重要概念。
强化用户体验与安全性:前端单点登录和统一认证的最佳实践与区别
|
数据安全/隐私保护 UED
SSO的优点是什么?
SSO的优点是什么?
182 0
|
存储 Web App开发 缓存
单点登录解决方案
单点登录在实际项目中应用的比较广泛,那么,遇到单点登录时,该怎样去解决呢
单点登录解决方案
|
存储 设计模式 缓存
权限管理系统,可以这么设计
权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源,不多不少。对权限做管理的系统,就是权限管理系统。
|
存储 缓存 安全
如何设计一套单点登录系统(下)
昨天介绍了API接口设计token鉴权方案,其实token鉴权最佳的实践场景就是在单点登录系统上。 在企业发展初期,使用的后台管理系统还比较少,一个或者两个。 以电商系统为例,在起步阶段,可能只有一个商城下单系统和一个后端管理产品和库存的系统。
|
存储 安全 Java
单点登录的原理
单点登录的原理
264 0

热门文章

最新文章