常用的认证&授权方法以及在kong上的实践

基本介绍

使用场景

基本介绍

使用场景

basic auth

HTTP Basic Auth 在HTTP中,基本认证是一种用来允许Web浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式,通常用户名和明码会通过HTTP头传递。在发送之前是以用户名追加一个冒号然后串接上口令,并将得出的结果字符串再用Base64算法编码。例如,提供的用户名是Aladdin、口令是open sesame,则拼接后的结果就是Aladdin:open sesame,然后再将其用Base64编码,得到QWxhZGRpbjpvcGVuIHNlc2FtZQ==。最终将Base64编码的字符串发送出去,由接收者解码得到一个由冒号分隔的用户名和口令的字符串。

参考:

https://blog.pytool.com/devops/oauth2/jwt/

简单,被广泛支持

不安全

安全分几个层面:内容的篡改及嗅探。这是HTTP协议本身存在的问题,所以很难根除,以后的网络世界会慢慢全部转为使用更加安全的HTTPS的。

1 用户HTTP是在网络上裸奔的,所以这个基本认证的用户名和密码也是可以被人看到的,虽然它使用了Base64来编码,但这个编码很容易就可以解码出来。

2 即使这个认证内容不能被解码为原始的用户名和密码也是不安全的,恶意用户可以再获取了认证内容后使用其不断的享服务器发起请求,这就是所谓的重放攻击。

3 像中间人攻击就更不能防止了,中间人可以修改报文然后请求服务器。

内部网络,或者对安全要求不是很高的网络。现如今HTTP基本认证都是会结合HTTPS一起使用的,https保证网络的安全性,然后基本认证来做客户端身份识别。

在结合了HTTPS后,Basic Authentication 可以说还是有一定的市场的,但是其重要性正在降低。因为合适的使用场景太少。我们可以想象一下:如果服务器是允许匿名用户访问的,那你就没有必要认证。如果服务器是不允许匿名访问的,那么需要用户注册,就会使用用户凭证认证,也不需要基本认证。只有那种只需要一个特定密码就可以访问的场景,例如加了提取码的网盘资源。

key auth 同上,只不过不用每次验证密码,第一次登录成功后拿到key和密匙,后续只需要每次带着,验证密匙通过,就可以访问了 同上 同上 感觉更适用于面向开发人员,拿到密匙后可以调用第三方的api了
hmac

HMAC技术,这个东西来自于MAC – Message Authentication Code,是一种用于给消息签名的技术,也就是说,我们怕消息在传递的过程中被人修改,所以,我们需要用对消息进行一个MAC算法,得到一个摘要字串,然后,接收方得到消息后,进行同样的计算,然后比较这个MAC字符串,如果一致,则表明没有被修改过(整个过程参看下图)。而HMAC – Hash-based Authenticsation Code,指的是利用Hash技术完成这一工作,比如:SHA-256算法。

  1. 客户端需要在认证服务器中预先设置 access key(AK 或叫 app ID) 和 secure key(SK)
  2. 在调用 API 时,客户端需要对参数和 access key 进行自然排序后并使用 secure key 进行签名生成一个额外的参数 digest
  3. 服务器根据预先设置的 secure key 进行同样的摘要计算,并要求结果完全一致
  4. 注意 secure key 不能在网络中传输,以及在不受信任的位置存放(浏览器等)

    参考:https://zhuanlan.zhihu.com/p/60522006

    HTTP API 认证授权术

安全,可以防止重放攻击 没有统一的标准,各家实现不一致 Acquia 的 HMAC,微信的签名算法
jwt

JWT是一个比较标准的认证解决方案,这个技术在Java圈里应该用的是非常普遍的。JWT签名也是一种MAC(Message Authentication Code)的方法。JWT的签名流程一般是下面这个样子:

  1. 用户使用用户名和口令到认证服务器上请求认证。
  2. 认证服务器验证用户名和口令后,以服务器端生成JWT Token,这个token的生成过程如下:
    • 认证服务器还会生成一个 Secret Key(密钥)
    • 对JWT Header和 JWT Payload分别求Base64。在Payload可能包括了用户的抽象ID和的过期时间。
    • 用密钥对JWT签名 HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+'.'+Base64UrlEncode(JWT-Payload));
  3. 然后把 base64(header).base64(payload).signature 作为 JWT token返回客户端。
  4. 客户端使用JWT Token向应用服务器发送相关的请求。这个JWT Token就像一个临时用户权证一样。

当应用服务器收到请求后:

  1. 应用服务会检查 JWT Token,确认签名是正确的。
  2. 然而,因为只有认证服务器有这个用户的Secret Key(密钥),所以,应用服务器得把JWT Token传给认证服务器。
  3. 认证服务器通过JWT Payload 解出用户的抽象ID,然后通过抽象ID查到登录时生成的Secret Key,然后再来检查一下签名。
  4. 认证服务器检查通过后,应用服务就可以认为这是合法请求了。

参考:

HTTP API 认证授权术

https://blog.pytool.com/devops/oauth2/jwt/

《JSW:The Complete Guide to JSON Web Tokens》

1.体积小,因而传输速度快
2.传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输
3.严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。
4.支持跨域验证,可以应用于单点登录。

1.Token有长度限制
2.Token不能撤销

JWT的主要优势在于使用无状态、可扩展的方式处理应用中的用户会话。服务端可以通过内嵌的声明信息,很容易地获取用户的会话信息,而不需要去访问用户或会话的数据库。在一个分布式的面向服务的框架中,这一点非常有用。
oauth2.0 上面的这些API认证都是要向Client发一个密钥(或是用密码)然后用HASH或是RSA来签HTTP的请求,这其中有个主要的原因是,以前的HTTP是明文传输,所以,在传输过程中很容易被篡改,于是才搞出来一套的安全签名机制,这些认证的玩法是可以在HTTP明文协议下玩的。

这种使用签名方式大家可以看到是比较复杂的,所以,对于开发者来说,也是很不友好的,在组织签名的那些HTTP报文的时候,各种,URLEncode和Base64,还要对Query的参数进行排序,然后有的方法还要层层签名,非常容易出错,另外,这种认证的安全粒度比较粗,授权也比较单一,对于有终端用户参与的移动端来说也有点不够。所以,在2012年的时候,OAuth 2.0 的 RFC 6749 正式放出。

OAuth 2.0依赖于TLS/SSL的链路加密技术(HTTPS),完全放弃了签名的方式,认证服务器再也不返回什么 secret 的密钥了,所以,OAuth 2.0是完全不同于1.0 的,也是不兼容的

参考:

同上

实施代码量小

维护工作减少

可以比较方便的接入第三方登录

Facebook 的 Graph API 只支持OAuth 2.0协议,Google 和 Microsoft Azure 也支持Auth 2.0,国内的微信和支付宝,微博等也支持使用OAuth 2.0。

更细粒度的权限验证

只能在https下使用

OAuth2是一个安全框架,描述了在各种不同场景下,多个应用之间的授权问题。有海量的资料需要学习,要完全理解需要花费大量时间。

OAuth2不是一个严格的标准协议,因此在实施过程中更容易出错。

第三方授权登录

kong的版本1.2.1,新版本或者旧版本可能有不同,可以看官文档

一、OAuth2.0

1.基本认识

概念:http://www.ruanyifeng.com/blog/2019/04/oauth_design.html

https://www.cnblogs.com/bigben0123/p/8334824.html

2.kong插件使用

1.如果没有consumer先创建consumer,再创建凭证

name:自己定义

client_id:可以自定义,空着会自动生成,获取accesstoken时需要

client_secret:可以自定义,空着会自动生产,获取accesstoken时需要

redirect_urls:如果是密码方式,用不到,可以随便填个url,后面的授权等用到时再讲

下图是创建完凭证后的信息

2.以同一个service的不同routes为例,先创建2个routes: resource和auth

在resource-route上启用oauth2.0插件

scopes:权限,自定义的

mandatory scope:控制验证scopes的可选布尔值

1
provision key:获取access_key是需要,空着后面会自动生成
1
token expiration:    token的过期时间
1
enable authorization code:打开授权码模式
1
enable implicit grant:打开隐藏式
1
enable client credentials:打开凭证模式
1
enable password grant:打开密码模式
1
global credentials:所有的oauth2插件是否可以共用一个token,默认false
1
auth_header_name                    发送请求时,请求头部携带token的参数 默认 authorization
1
hide_credentials                    是否向service隐藏授权信息     默认false。
1
accept_http_if_already_terminated
1
anonymous                            可以匿名访问的用户id(kong的用户),只有在认证失败的请款下才会用到此参数
1
refresh_token_ttl                    refresh_token的失效时间 默认 1209600s, refresh_token顾名思义,可以拿这个去更新access_token,之前的access_token会全作废,生成新的access_token和refresh_token
1
<wyn>

1.密码方式

1
打开enable password grant

上图是官方所给出的密码方式流程图,但是把后端脱离了kong的管理,所以使用下面的图来修改并补充下具体的流程

backend 就是用户验证服务,验证用户名和密码,这个可以新建个routes或者service,不安装oauth2.0插件,只是通过kong转发。

resource_service 是资源服务,需要安装oauth2.0插件,用户想访问它需要登录拿到token,才可以通过kong插件的验证,没有token无法访问

验证代码

测试如上图,访问auth去获取access_token,接下来用access_token去访问resource才可以。否则401,如下图

接下来将我们获取到的token,填到头部,访问成功

2.授权码

官方给出的流程如下

1.建消费者, 凭证选择oauth2.0

2.在resource-route建立插件,打开enable authorization code(资源服务器host-A上开启插件)

3.这时候我们想获取资源是告诉我们需要认证的, 然后假设我们跳转到了授权页面(在授权之前先检查client_id是否合法,向kong请求),如果没有登录的话,还让我们登录,已登录就会直接跳出授权页面,自己想象, 这里这个url http://host-B/authorize?response_type=code&scope=read&client_id=7W3uf7vuzbkPZJ7c74OIMC1Z57GPot41当一点击send(授权)会请求这个认证服务(host-B)的后端接口,然后认证后端接口解析参数,并向kong发送下图的请求, 获取到我们上图设置的redirect_urls和code,返回给认证后端,这个redirect_uri生产环境也应该是一个认证后端的接口,前端可以直接访问这个uri传递code,即可

换取access_token等信息,为了测试就随便写了个uri

4.这一步假设是前端传递了code到上面设置的uri了,后端会按下面的方式去换取token并返回给前端

这个code和上面返回的code不一样是怎么回事?

因为code只能用一次, 再用就会报下图的错误,上一个没截图,又生成了一个新code去换取了token

5.用token成功获取到资源,这里我又重新获取了一次。。。没保存,所以和上面的token截图不一样。

3.隐藏式

有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"(implicit)。

这种方式把令牌直接传给前端,是很不安全的。因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。

4.凭证式

适用于没有前端的命令行应用,即在命令行下请求令牌。

1
2
3
4
https://oauth.b.com/token?
  grant_type=client_credentials&
  client_id=CLIENT_ID&
  client_secret=CLIENT_SECRET

上面 URL 中,grant_type参数等于client_credentials表示采用凭证式,client_idclient_secret用来让 B 确认 A 的身份。

第二步,B 网站验证通过以后,直接返回令牌。

这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。

1
<wyn>

二、Hmac Authentication

1.基本认识

2.kong插件使用

1.在之前创建的消费者上创建hmac证书

username:wgy2 hmac签名验证时用

secret:wgy2 hmac签名验证时用,如果为空,kong会自动生成

2.在routes上启用hmac auth插件

clock skew:使用Kong的hmac后,请求必须带有Date和x-date请求头,这个是防止重放攻击(Replay Attacks),默认的情况下,客户端传过来的时间和服务端的时间相差300s,Kong就认为这个请求是有问题的。这个值可以修改的

enforce_headers:http请求头验证需要哪些字段生成签名

algorithms:支持哪些加密算法

validate request body:是否校验请求body

3.测试

1.直接使用提示缺少date或者x-date字段,因为clock skew的设置,所以我们加上这个date

2.加上date后,响应变成了验证不过了, 注意这里的时间需要用GMT格式的

3.

一、头部验证

签名的生成:

输出如下:hCgpKdqq73PaY6XD6xajwtIQIdaCElT739vXcYC74U4=

所以头部如下:

1
  Authorization: hmac username="wgy2", algorithm="hmac-sha256", headers="date request-line", signature=hCgpKdqq73PaY6XD6xajwtIQIdaCElT739vXcYC74U4="

username: 凭证的username

algorithm: 指定的算法, 必须是在插件设置的一个或多个

headers:要验证的头部,需要用这2个去计算签名

signature:计算出来的签名

这是请求直接报EOF了,这是因为我们打开了body的验证,但是body没传任何东西,所以接下来需要传输body并且计算body的签名

二、验证请求body

计算body,这里我传的是

计算body的签名:

输出如下:nRAOg2ol9S2HTrC/r3qf/k5/FGFs9ITToMIqC00KHEk=

在header中加入Digest:nRAOg2ol9S2HTrC/r3qf/k5/FGFs9ITToMIqC00KHEk= 。然后访问成功

注意:Date字段设置的与服务端相差300s就会被拒绝,所以这个文档中看到的可能一直在变,是因为我多次测试,过了五分钟需要更新Date和签名

这个看起来secret需要客户端和服务端分别存? 客户端存储有被反编译的风险吧?

三、JWT

1.基本认识

http://blog.leapoahead.com/2015/09/06/understanding-jwt/

2.kong上的jwt插件使用

1.创建消费者

2.创建jwt凭证

key和secret可以自定义,推荐kong自动生成

rsa_public_key 使用RS256算法时才需要

生成的key,secret

3.创建jwt插件

基本使用默认的就可以

4.测试

1.未通过验证

2.计算jwt签名,生产环境中,这步应该是验证密码通过后,后端计算签名,并返回给客户端

测试使用https://jwt.io/

header:

alg:算法,创建时选的算法

ty:jwt

playload:

iss字段是步骤3中的key claim name,可以自定义,值就是步骤2生成的key

蓝色字体框里面的就是填步骤2生成的secret

3.将生成的签名添加到头部,访问成功

Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRc2pieFEzUExORWxlUHhrZUIwYkhpOURhSnF0NEU1eSIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.zDs-BC-jQDT5X51VBZqcMi88lrDHMEYRozjELqEUSoA

四、Basic Authentication

1.基本认识

https://blog.csdn.net/ShuSheng0007/article/details/89598299

2.kong上的Basic Authentication插件使用

1.创建消费者 username: wgy1 custom_id:wgyid1

2.创建凭证 username:wgy1 password:wgy1

3.在routes上启用basic Authentication插件,

4.测试

响应内容为未验证

在请求的头部中加入步骤2中的username:password的base64编码的验证信息

echo "wgy1:wgy1"|base64
d2d5MTp3Z3kxCg==

显示验证成功

3.总结:

优:

劣:

五、Key Auth

1.基本认识

2.kong上的插件使用

1.同上创建消费者 wgy2

2.创建凭证,设置value

3.在routes启用插件,设置key

4.测试

1.直接测试,提示key没有被发现

2.加入header,提示成功

3.也可以使用querystring parameter:

下一遍:kong自定义插件