二级域名分发系统(Kong)

目录登录实现B 端登录之后,浏览器存 cookie登录代码实现细节,cookie设计网关介绍API 网关是什么为什么需要网关从技术角度来看,什么是Kong?为什么使用 KongKong 网关解析 cookiekong 项目简介,流量转发鉴权 lua 脚本服务解析请求此方案实现的优缺点单点登录问题登录续期问题注销问题登录实现B 端登录之后,浏览器存 cookie登录后的cookiecookie 内容如下:key: xxx_V2value: 3001459%7C1636684996%7C7180720502%7Cb61a12ef865072964aa359e6a9ef2e0b1846dee9value 分为四段值,由 %7C 隔开,这个Ascll码值是代表符号"|".四段分别代表 userId, 时间戳 time, 随机数 nonce, 加密算法得到的 sign登录代码实现细节,cookie设计/**
* 用户登录逻辑
* @global array $_SESSION
* @param type $user_id
*/
public static function do_login($user_id) {
global $_SESSION;
$_SESSION["uid"] = $user_id;
KZUser::add_auth_to_session($user_id);

// 更新帐号失败尝试次数
unset($_SESSION["login_try"]);
unset($_SESSION["need_vcode"]);

//send the cookie
$login_time = time();
$nonce = mt_rand(1000000000, 9999999999);
$cookie_params = array($user_id, $login_time, $nonce);
// COOKIE_KEY 即加密的密钥,和网关那边配置的一样
$cookie_params[] = hash_hmac("sha1", implode("", $cookie_params), COOKIE_KEY, false);
// 测试环境特殊处理
$domain = str_replace(".t1.com", DOMAIN_POSTFIX, COOKIE_DOMAIN);
// SESSION_COOKIE_NAME=xxx_V2
setcookie(SESSION_COOKIE_NAME, implode("|", $cookie_params), $login_time + 604800, "/", $domain, false, true);
}
上面就是计算 setcookie 函数具体实现。$cookie_params[] = hash_hmac("sha1", implode("", $cookie_params), COOKIE_KEY, false);
cookie 最后一个值 sign,会通过加密计算,利用 sha1 算法进行加密计算得到一个16 进制字符串得到sign。其中 COOKIE_KEY 即加密的密钥,和网关那边配置的一样。网关那边会通过同样的加密算法,加密参数,加密密钥进行加密计算,如果参数一样那么算出来的sign值会和cookie一致,那么说明是有效的cookie。 setcookie(SESSION_COOKIE_NAME, implode("|", $cookie_params), $login_time + 604800, "/", $domain, false, true);
最后数组用"|"连接成一个字符串即得到前面的四段 cookie 值。网关介绍API 网关是什么API网关是随着微服务(Microservice)概念兴起的一种架构模式。原本一个庞大的单体应用(All in one)业务系统被拆分成许多微服务(Microservice)系统进行独立的维护和部署,服务拆分带来的变化是 API 的规模成倍增长,API的管理难度也在日益增加,使用API网关发布和管理API逐渐成为一种趋势。一般来说,API网关是运行于外部请求与内部服务之间的一个流量入口,实现对外部请求的协议转换、鉴权、流控、参数校验、监控等通用功能。为什么需要网关微服务架构下,单体应用被切割成多个微服务,如果将所有的微服务直接对外暴露,势必会出现安全方面的各种问题。客户端如果可以直接向每个微服务发送请求,其问题主要如下:客户端需求和每个微服务暴露的细粒度 API 不匹配。每个公司可能有java,python,go多种语言,在不同的场景下又会使用不同的协议,部分服务使用的协议不是 Web 友好协议,可能使用 Thrift 二进制 RPC,也可能使用 AMQP 消息传递协议,或者其他的 grpc 之类的协议。微服务的划分可能随着时间变化,微服务难以重构。如果合并两个服务,或者将一个服务拆分成两个或更多服务,这类重构就非常困难了。不同的客户端可能需要不同的数据,例如 Web,H5,APP,Android,IOS。网关开源组件业内用的多的有 Zuul、SpringCloud Gateway、Kong、Nginx 等。从技术角度来看,什么是Kong?❝这一段摘自KONG官方介绍文档您可能已经听说过Kong基于Nginx,利用了其稳定性和高效率。但这究竟是怎么实现的呢?更确切地说,Kong 是一个在 Nginx 中运行的 Lua 应用程序,并且可以通过 lua-nginx模块实现。Kong不是用这个模块编译Nginx,而是与 OpenResty 一起分发,OpenResty已经包含了lua-nginx-module。OpenResty 不是 Nginx 的分支,而是一组扩展其功能的模块。这为可插拔架构奠定了基础,可以在运行时启用和执行Lua脚本(称为“插件”)。 因此,我们认为Kong是微服务架构的典范:它的核心是实现数据库抽象,路由和插件管理。 插件可以存在于单独的代码库中,并且可以在几行代码中注入到请求生命周期的任何位置。❞简而言之,Kong 是 Mashape 开源的高性能高可用 API 网关和 API 服务管理层,一款基于 Nginx_Lua 模块写的高可用服务网关,由于 Kong 是基于 Nginx 的,所以可以水平扩展多个 Kong 服务器。通过前置的负载均衡配置把请求均匀地分发到各个 Server,来应对大批量的网络请求。为什么使用 Kong在众多 API GATEWAY 框架中,Mashape 开源的高性能高可用 API 网关和 API 服务管理层——KONG(基于 NGINX)特点尤为突出,它可以通过插件扩展已有功能,这些插件(使用 lua 编写)在API请求响应循环的生命周期中被执行。于此同时,KONG本身提供包括 HTTP 基本认证、密钥认证、CORS、TCP、UDP、文件日志、API请求限流、请求转发及 NGINX 监控等基本功能。除了Kong的基本网关功能,Kong 的云原生属性:与平台无关,Kong 可以从裸机运行到 Kubernetes,是我们青睐的原因,我们的服务都是 k8s 部署调度的。Kong 经常用到的术语有:client : 指下游客户向 Kong 代理端口发出请求。upstream service :指自己位于 Kong 后面的 API/Service,客户端请求被转发到这些API/Service。Service : 顾名思义,服务实体是上游服务的抽象。服务的示例包括数据转换微服务、计费API等。Route : 这是指 Kong 路由实体。Route 是进入 Kong 的入口点,并为要匹配的请求定义规则,然后路由到给定的服务。Plugin : 指 Kong 的 “plugins”,它是在代理生命周期中运行的业务逻辑片段。插件可以通过管理 API 进行配置——可以是全局的(所有传入的流量),也可以是在特定的路由和服务上配置。Kong 网关解析 cookiekong 项目简介,流量转发Kong 项目构成这个项目只是做了鉴权,属于内网网关,流量在这之前还会经过一道外网网关,那边有流控,请求分发,配置证书等功能,内网网关只是做鉴权,流量打到这边鉴权之后不同的路由转发到 k8s 的不同 service 里调用具体的服务。项目部署了线上网关和测试环境网关两种环境,提供的plugins插件鉴权有 APP端的鉴权,C端和B端鉴权,这里我主要讲的是B端的鉴权,其他的简单介绍一下:shop-resolve 里面主要是 小程序和H5 两种环境下的店铺信息鉴权,是公司一个电商项目需要用的;idk-client-type-resolve 里面主要是 小程序和H5 两种环境下的访问鉴权;health-check 没什么好说的,是k8s的健康检查需要的其他三个 app-resolve,buser-reslove,cuser-resolve 分别是app环境,B端,C端的鉴权kong.ymal-servicekong.ymal 里面配置了很多业务线的 Service,比如快站云服务可以通过路由(www.kuaizhan.com,cloud.kuaizhan.com)过来,配置里面使用了鉴权插件 bUser-resolve,根据这个 name 配置请求转发 upstream service 如下:kong.ymal-upstream serviceupstream service 指自己位于 Kong 后面的 API/Service,客户端请求被转发到这些API/Service,此处是转发到 k8s 的 service 流量入口到具体的服务处理请求。鉴权 lua 脚本B端鉴权lua脚本local ngx_re = require "ngx.re"
local BasePlugin = require "kong.plugins.base_plugin"
local string = require "resty.string"
local BuserResolveHandler = BasePlugin:extend()

function BuserResolveHandler:new()
BuserResolveHandler.super.new(self, "buser-resolve")
end

function BuserResolveHandler:access(conf)
BuserResolveHandler.super.access(self)
local cookieName = "cookie_" .. "KUAIZHAN_V2"
local kuaizhanV2 = ngx.var[cookieName]
— ngx.log(ngx.ERR, "kuaizhanV2", kuaizhanV2)
local uid = 0
ngx.req.set_header("X-User-Id", uid)

// cookie 内容 3001459%7C1636684996%7C7180720502%7Cb61a12ef865072964aa359e6a9ef2e0b1846dee9
if xxx_V2 and xxx_V2 ~= '' then
local res, err = ngx_re.split(xxxV2, "%7C")
if err then
return
end
// 解析得到 userId, time, nonce, sign,
// 分别为 3001459,1636684996,7180720502,b61a12ef865072964aa359e6a9ef2e0b1846dee9
local userId, time, nonce, sign = res[1], res[2], res[3], res[4]
// 根据密钥,时间,签名,利用 hmac_sha1 算法,十六进制算法,得到摘要签名
local digest = ngx.hmac_sha1(conf.secret, userId .. time .. nonce)
local theSign = string.to_hex(digest)
— TODO 加上过期时间判断
// 计算签名和cookie解析得到sign 是否相同,相同则赋值uid
if theSign == sign then
uid = userId
end
ngx.log(ngx.ERR, "theSign:", theSign, "sign:", sign, "uid:", uid)
end

ngx.log(ngx.ERR, "get x-user-id:" .. uid)
// nginx 请求头header里面存放 X-User-Id,再转发到各个业务线
ngx.req.set_header("X-User-Id", uid)
end

return BuserResolveHandler
解析过程如下四步:cookie 内容 3001459%7C1636684996%7C7180720502%7Cb61a12ef865072964aa359e6a9ef2e0b1846dee9解析得到 userId, time, nonce, sign, 分别为 3001459,1636684996,7180720502,b61a12ef865072964aa359e6a9ef2e0b1846dee9根据密钥,用户id,时间,随机数,利用 hmac_sha1 算法,十六进制算法,得到摘要签名,计算签名和cookie解析得到sign 是否相同,相同则赋值uidnginx 请求头header里面存放 X-User-Id,再转发到各个k8s service 中去(k8s service是各个服务集群的流量入口)conf.secret 里面配置的密钥,很多业务线公用一个登录的话,比如官网很多业务线,那么插件的 secret 都是一样的配置,同理,这个 secret 和刚刚设置 cookie 的时候,使用的是一个值。服务解析请求因为这种方式在网关层就实现了在 header 里面设置了用户id信息,到了各个业务方直接写一个解析器解析请求头的 userId;然后写一个注解类似于 @LoginRequired 的自定义注解,配合变量 BUser 来使用,这个 BUser 对象里就包含解析得到的 userId;最后注解作用于 controller 接口,就可以完成请求的登录信息拦截了。由于这个实现太偏业务,而且比较简单这里就不贴具体的代码实现了。此方案实现的优缺点优点:实现简单,易维护易懂可以实现统一网关鉴权和流量分发单点登录问题一个企业一般情况下只有一个域名,通过二级域名区分不同的系统。比如我们主域名叫做 xxx.com,另一个业务线有个二级域名 aaa.xxx.com,现在要实现在官网登录了,那么也就在另一个业务线登录了。实现如下:登录以后,可以将Cookie的域设置为顶域 xxx.com,这样所有子域的系统都可以访问到顶域的 Cookie。我们在设置 Cookie 时,只能设置顶域和自己的域,不能设置其他的域。因此这边另一个业务线可以直接访问到顶域的登录状态,然后在 kong 解析那边的访问请求,密钥设置成一样的就可以鉴权了。但是多端,或者跨顶域情况下的单点登录是没法做的。登录续期问题不知道大家有没有关注到第一张 cookie 截图后面有个失效时间,这种由于依赖 cookie,因此只要到期就会被踢下线,需要重新登录,一定程度上影响客户体验。现在的很多应用都利用 token + redis 方案实现了登录续期,例如连续十天内有过登录,那么不需要再次登录,后端实现自动续期,十天以上都没有登录过的才失效需要重新登录。但是续期服务需要 redis 成本,这种省成本。注销问题这种方案的注销,只是简单的删除 cookie,如果有心人拿到 cookie 仍然是可以用的,这个 cookie 在有效期内不会失效。token+redis 方案可以做到真实的 token 失效。以上就是此篇分享的所有内容呢了,我是《小龙飞》公众号号主小飞龙,欢迎关注我的公众号查看更多往期精彩内容,原创不易,如果对您有收获可以点个赞点个在看鼓励一下号主吧~~~备注:文章中部分专业解释参考 kong 官方文档

本文出自快速备案,转载时请注明出处及相应链接。

本文永久链接: https://www.175ku.com/27403.html