作者:微信小助手
发布时间:2021-11-23T15:05:40
目录 登录实现 B 端登录之后,浏览器存 cookie 登录代码实现细节,cookie设计 网关介绍 API 网关是什么 为什么需要网关 从技术角度来看,什么是Kong? 为什么使用 Kong Kong 网关解析 cookie kong 项目简介,流量转发 鉴权 lua 脚本 服务解析请求 此方案实现的优缺点 单点登录问题 登录续期问题 注销问题 cookie 内容如下: value 分为四段值,由 %7C 隔开,这个Ascll码值是代表符号"|". 四段分别代表 userId, 时间戳 time, 随机数 nonce, 加密算法得到的 sign 上面就是计算 setcookie 函数具体实现。 cookie 最后一个值 sign,会通过加密计算,利用 sha1 算法进行加密计算得到一个16 进制字符串得到sign。 其中 COOKIE_KEY 即加密的密钥,和网关那边配置的一样。网关那边会通过同样的加密算法,加密参数,加密密钥进行加密计算,如果参数一样那么算出来的sign值会和cookie一致,那么说明是有效的cookie。 最后数组用"|"连接成一个字符串即得到前面的四段 cookie 值。 API网关是随着微服务(Microservice)概念兴起的一种架构模式。原本一个庞大的单体应用(All in one)业务系统被拆分成许多微服务(Microservice)系统进行独立的维护和部署,服务拆分带来的变化是 API 的规模成倍增长,API的管理难度也在日益增加,使用API网关发布和管理API逐渐成为一种趋势。 一般来说,API网关是运行于外部请求与内部服务之间的一个流量入口,实现对外部请求的 微服务架构下,单体应用被切割成多个微服务,如果将所有的微服务直接对外暴露,势必会出现安全方面的各种问题。 客户端如果可以直接向每个微服务发送请求,其问题主要如下: 网关开源组件业内用的多的有 Zuul、SpringCloud Gateway、Kong、Nginx 等。 这一段摘自KONG官方介绍文档 Kong不是用这个模块编译Nginx,而是与 OpenResty 一起分发,OpenResty已经包含了lua-nginx-module。OpenResty 不是 Nginx 的分支,而是一组扩展其功能的模块。 因此,我们认为Kong是微服务架构的典范:它的核心是实现数据库抽象,路由和插件管理。插件可以存在于单独的代码库中,并且可以在几行代码中注入到请求生命周期的任何位置。 简而言之, 在众多 API GATEWAY 框架中,Mashape 开源的高性能高可用 API 网关和 API 服务管理层——KONG(基于 NGINX)特点尤为突出,它可以通过插件扩展已有功能,这些插件(使用 lua 编写)在API请求响应循环的生命周期中被执行。 于此同时,KONG本身提供包括 HTTP 基本认证、密钥认证、CORS、TCP、UDP、文件日志、API请求限流、请求转发及 NGINX 监控等基本功能。 除了Kong的基本网关功能,Kong 的云原生属性:与平台无关,Kong 可以从裸机运行到 Kubernetes,是我们青睐的原因,我们的服务都是 k8s 部署调度的。 Kong 经常用到的术语有: 这个项目只是做了鉴权,属于内网网关,流量在这之前还会经过一道外网网关,那边有流控,请求分发,配置证书等功能,内网网关只是做鉴权,流量打到这边鉴权之后不同的路由转发到 k8s 的不同 service 里调用具体的服务。 项目部署了线上网关和测试环境网关两种环境,提供的plugins插件鉴权有 APP端的鉴权,C端和B端鉴权,这里我主要讲的是B端的鉴权,其他的简单介绍一下: kong.ymal 里面配置了很多业务线的 Service,比如快站云服务可以通过路由(www.kuaizhan.com,cloud.kuaizhan.com)过来,配置里面使用了鉴权插件 bUser-resolve,根据这个 name 配置请求转发 upstream service 如下: upstream service 指自己位于 Kong 后面的 API/Service,客户端请求被转发到这些API/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 失效。
登录实现
B 端登录之后,浏览器存 cookie
登录代码实现细节,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);
}$cookie_params[] = hash_hmac("sha1", implode("", $cookie_params), COOKIE_KEY, false);
setcookie(SESSION_COOKIE_NAME, implode("|", $cookie_params), $login_time + 604800, "/", $domain, false, true);
网关介绍
API 网关是什么
协议转换、鉴权、流控、参数校验、监控等通用功能
。为什么需要网关
从技术角度来看,什么是Kong?
❝
您可能已经听说过Kong基于Nginx,利用了其稳定性和高效率。但这究竟是怎么实现的呢?
更确切地说,Kong 是一个在 Nginx 中运行的 Lua 应用程序,并且可以通过 lua-nginx模块实现。
这为可插拔架构奠定了基础,可以在运行时启用和执行Lua脚本(称为“插件”)。Kong 是 Mashape 开源的高性能高可用 API 网关和 API 服务管理层,一款基于 Nginx_Lua 模块写的高可用服务网关,由于 Kong 是基于 Nginx 的,所以可以水平扩展多个 Kong 服务器。通过前置的负载均衡配置把请求均匀地分发到各个 Server,来应对大批量的网络请求。
为什么使用 Kong
Kong 网关解析 cookie
kong 项目简介,流量转发
鉴权 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
服务解析请求
此方案实现的优缺点
单点登录问题
登录续期问题
注销问题