目录
一、API网关简介
- 什么是api 网关:
- 我们知道在微服务架构中,大型服务都被拆分成了独立的微服务,每个微服务通常会以RESTFUL API的形式对外提供服务
- 但是在UI方面,我们可能需要在一个页面上显示来自不同微服务的数据,此时就会需要一个统一的入口来进行API的调用
- API Gateway就在此场景下充当了多个服务的大门,系统的统一入口
- api 网关应该具备的功能
- 服务路由
- 动态路由:API Gateway可以与微服务注册中心连接,实现微服务无感知动态扩容
- 负载均衡
- 服务发现
- 熔断降级:API Gateway对于无法访问的服务,可以做到自动熔断,无需人工参与
- 黑白名单过滤
- 服务路由
- api网关技术选型
API网关 | Kong | APISIX | Trk | Apigee | Aliyun |
---|---|---|---|---|---|
部署模式 | 单机和集群 | 单机和集群 | 单机和集群 | 不支持单机 | RaaS |
数据存储 | Postgres或者Cassandra | etcd | Redis | Postgres、Cassandra和Zookeeper | RaaS |
是否开源 | Apache 2.0协议 | Apache 2.0协议 | MPL协议 | 否 | 否 |
核心技术 | Nginx+Lua | Nginx+Lua | Golang | 未知 | 未知 |
私有部署 | 是 | 是 | 是 | 否 | 否 |
自定义插件 | 是 | 是 | 是 | 否 | 否 |
社区活跃度 | 高 | 高 | 高 | 中 | 低 |
支持yaml | 是 | 是 | 否 | 否 | 否 |
二、kong的安装和配置
- kong官方地址:https://github.com/Kong/kong
- kong中文翻译文档:https://github.com/qianyugang/kong-docs-cn
- 知乎上关于kong的集合:https://www.zhihu.com/topic/20174970/hot
- kong参考地址:https://blog.csdn.net/qq23001186/article/details/126357870
三、动态路由
1 - kong的端口说明
- 8001:kong的管理端口
- 8000:用户访问的端口
- 1337:konga地址
2 - 动态路由实现
- SERVICES:
- Service顾名思义就是我们自己定义的上游服务,通过kong匹配到相应的请求要转发的地方
- Service可以与下面的Router进行关联,一个Service可以有很多Router,匹配到的Router就会转发到Service中
- 当然中间也会通过Plugin的处理,增加或者减少一些相应的Header或者其他信息
- ROUTERS
- Router路由相当于nginx配置中的location
- Router实体定义匹配客户端请求的规则,每个路由都与一个服务相关联,而服务可能有多个与之相关联的路由
- 每一个匹配给定路线的请求都将被提交给它的相关服务
- 路由和服务的组合(以及它们之间的关注点分离)提供一种强大的理由基址,可以在kong中定义细粒度的入口点
- 从而引导访问到不同的upstream服务
- 添加Service
- 为Service添加Router
- 测试kong的路由服务:开启goods_srv、goods_web,http://192.168.124.51:8000/g/v1/goods
- goods_web/initialize/init_router.go:修改下路由规则,去掉/g
package initialize
import (
"github.com/gin-gonic/gin"
"web_api/goods_web/middlewares"
"web_api/goods_web/router"
)
func Routers() *gin.Engine {
Router := gin.Default()
//配置跨域
Router.Use(middlewares.Cors())
//ApiGroup := Router.Group("/g/v1")
ApiGroup := Router.Group("/v1")
router.InitGoodsRouter(ApiGroup)
router.InitCategoryRouter(ApiGroup)
router.InitBannerRouter(ApiGroup)
router.InitBrandRouter(ApiGroup)
return Router
}
- konga的router中添加/g:记得回车并提交
3 - service、rouer、upstream请求过程
- kong三种访问服务的方式
- 硬编码方式:这个在之前的动态路由里面实现的
- 通过upstream实现
- 通过consul实现
- 额外说明
- routers路由匹配客户端的请求规则,匹配成功后分配到service层,一个路由指向一个service,一个service可以被多个不同规则的路由router指向
- 访问地址:kong网关地址+代理端口(默认http=8000,https=8443)
- service服务是一个抽象服务层,可以用于指向具体物理服务(target),也可以指向upstream用于实现物理服务的负载效果,一个service对于upstream、target都是一对一的关系
4 - kong集成consul服务发现与负载均衡
- 浏览器访问:开启goods_srv、goods_web,[http://192.168.124.51:8000/g/v1/goods];我们可以看到依然可以访问
- 测试负载均衡:
- goods_web/main.go:修改为port随机,之前我们是写死的,
flag = 5 // 这里为了测试kong的负载均衡
- 开启2个goods_web服务,然后浏览器访问2次,我们可以看到每个服务各请求了一次
- goods_web/main.go:修改为port随机,之前我们是写死的,
四、kong配置jwt实现登录校验
1 - 原理分析
- 需求分析:之前我们已经做了jwt的验证,为什么还要在kong中使用jwt?
- 很简单,因为api网关是统一的入口,我们只需要在网关中实现jwt验证,那么在业务中我们就不需要在做jwt校验了
- 通用认证:一般情况下,上游api服务器都需要客户端有身份认证,且不允许错误的认证或无法认证的请求通过;认证插件可以实现这一需求
- 通用认证插件的方案/流程
- ①.向一个api或全局添加AUTH插件(此插件不作用域consumers)
- ②.创建一个consumers对象
- ③.为consumer提供指定的验证插件方案的身份验证凭据
- ④.现在,只要有请求进入kong,都将检查其提供的身份验证凭据(取决于auth类型),如果该请求无法被验证或者验证失败,则请求会被锁定,不执行向上游服务转发的操作
- 但是,上述的一般流程并不是总是有效的;比如,当使用了外部验证方案(比如LDAP)时,kong就不会(不需要)对consumer进行身份验证
- Counsumers:
- 最简单的理解和配置consumer的方式,将其与“用户”进行一一隐射,即一个consumer代表一个“用户”(这里指的是应用,不要理解成登录的用户)
- 但是对于kong而言,这些都无所谓;
- consumer的核心原则是你可以为其添加插件,从而自定义它的请求行为;
- 所以,或许你会有一个手机APP应用,并为他的每个版本都定义个consumer,又或者你有一个应用或几个应用,并为这些应用定义同一个consumer,这些都无所谓
- 注意:这是一个模糊的概念,它叫做consumer,而不是user,要区分清楚
2 - kong配置jwt
添加1个consumer
为这个consumer添加JWT
配置全局的PLUGIN:设置header为x-token
生成token:https://jwt.io/,添加key和secret
- 没有配置x-tocken时:返回"message": “Unauthorized”
- 配置x-token:将刚才在https://jwt.io/中生成token复制进来,并且前面要加上
Bearer
(这个是kong的要求)- 此时提示未登陆
- 未登陆的原因分析
- jwt的nacos配置中我们设置过secret了
- jwt的验证要确认key是不是“imooc”,也就是之前未consumer添加JWT验证时候key和secret要和gin对应
- 之前在用户服务时user_web/api/api_user.go,生成token的时候我们Issuer设置为的是“imooc”
- 这时候还有一个非常重要的修改:之前我们在token中因为kong的要求添加了前缀
Bearer
,所以在jwt中我们就需要分割去掉Bearer
- goods_web/middlewares/jwt.go
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localSstorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
token := c.Request.Header.Get("x-token")
if token == "" {
c.JSON(http.StatusUnauthorized, map[string]string{
"msg": "请登录",
})
c.Abort()
return
}
token = strings.Split(token, " ")[1]
j := NewJWT()
// parseToken 解析token包含的信息
claims, err := j.ParseToken(token)
//省略
五、kong实现反爬和ip黑名单
1 - 反爬
2 - ip黑名单
本文含有隐藏内容,请 开通VIP 后查看