之前有有一篇介绍了如何搭建kong服务器,参考ubuntu14.04 上kong服务器的搭建
目的
目前我们的系统有很多业务子系统,几乎都是rest api server,所以统一把认证在kong上做,另外我们独立开发了一个简单的sso(用户认证中心), 所有系统的登录统一在sso登录,拿到token,然后就可以通过kong去访问api服务器了.具体有以下流程和关键点
业务流程可以查看这个图:
场景一: 网站 - 用户未登录
- 客户在A网站访问需要登录跳转至Auth Server(sso)
- 返回url,next_url
- SSO系统验证用户未登录,跳转至登录页面
- 用户输入用户名密码登录
- 成功后,sso计算对称加密一个ticket,
- 在api gateway(kong)请求一个oauth的token,
- 同时将加密的ticket和同户名,access_token和refresh_token的信息一起存入redis.
- SSO系统本地登录
- 保存session.
- 将oauth信息保存到数据库.
- SSO将回调链接带上ticket参数返回A网站的链接
- A网站拿到ticket,再次在后台调用SSO的验证api,解密后的ticket参数
- SSO将ticket验证通过,返回用户名和oauth的信息,同时从redis里删除ticket的数据
- A网站拿到用户信息,本地登录,存session
- A网站将oauth信息保存到session和redis
场景二: 网站 - 用户登录A,再访问B
- 客户访问B网站
- 跳转到SSO的登录
- SSO的session发现此用户已经验证
- 成功后,sso计算一个对称加密的ticket,
- 数据库查询token信息,将加密的ticket和access_token和refresh_token一起存redis
- SSO将回调链接带上ticket参数返回A网站的链接
- B网站拿到ticket,再次调用SSO的验证api,带上ticket参数
- SSO将ticket验证通过,返回用户名和oauth的信息,同时从redis里删除ticket的数据
- A网站拿到用户信息,本地登录,存session
- 将oauth信息保存到session和redis
场景三: 手机和其它非web client - 用户未登录
- 调用登录API,传递用户名和密码
- SSO服务器验证用户合法性
- 通过登录,向Api gateway(kong)请求oauth信息
- 将oauth信息返回给客户端
综上所述kong上简要流程如下
- 为api开启oauth2认证
- 使用Resource Owner Password Credentials的授权流程
- 让我们业务系统的sso(用户中心)在成功登陆后,在kong server上获取用户的access_token和refresh_token,返回给sso
- sso将成功登录的消息,用户信息和token一起返回给业务系统(这一步省略)
配置过程
前提条件: kong的server已经准备好了,参考ubuntu14.04 上kong服务器的搭建
一.添加api
这里添加的方式不是host,是request_path
请求
curl -X POST -H "Cache-Control: no-cache" -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "name=docsapi" -F "upstream_url=http://docs.apiusage.com" -F "request_path=/docs" -F "strip_request_path=true" "http://10.0.0.202:8001/apis/"
响应
{
"upstream_url": "http://docs.apiusage.com",
"request_path": "/docs",
"id": "954b2dfc-e6d5-421b-a796-cd1d751f0af8",
"created_at": 1466920233000,
"preserve_host": false,
"strip_request_path": true,
"name": "docsapi"
}
添加完以后可以验证一下
curl -X GET -H "Cache-Control: no-cache" "http://10.0.0.202:8000/docs/online_docs/linux/rsync/rsync.html"
二.为api安装oauth2插件
请求
curl -X POST -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "name=oauth2" -F "config.token_expiration=720000" -F "config.enable_password_grant=true" -F "config.scopes=email,phone,address" "http://10.0.0.202:8001/apis/docsapi/plugins"
响应
{
"api_id": "954b2dfc-e6d5-421b-a796-cd1d751f0af8",
"id": "7734b163-721e-430f-9ad9-9dd4c5f47b71",
"created_at": 1466927983000,
"enabled": true,
"name": "oauth2",
"config": {
"mandatory_scope": false,
"token_expiration": 720000,
"enable_implicit_grant": false,
"scopes": [
"email",
"phone",
"address"
],
"hide_credentials": false,
"enable_password_grant": true,
"accept_http_if_already_terminated": false,
"provision_key": "758017a57c4447628546d0d266f92a9b",
"enable_client_credentials": false,
"enable_authorization_code": true
}
}
三.添加一个开发者账号
一个开发者账号是可以全站用的
请求
curl -X POST -H "Cache-Control: no-cache" -H "Postman-Token: 1636232e-7e6b-3ee5-8e5a-11ac4579f972" -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "username=vincent" -F "custom_id=1" "http://10.0.0.202:8001/consumers"
响应
{
"custom_id": "1",
"username": "vincent",
"created_at": 1466928362000,
"id": "ab7fcd95-fa06-456a-9e4a-b2b701a510de"
}
四.为开发者注册一个应用,用来访问api
请求
curl -X POST -H "Cache-Control: no-cache" -H "Postman-Token: b6bf0e87-3d98-e4fb-26f4-4711d61513e0" -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "name=openapidoc" -F "redirect_uri=http://some-domain/endpoint/" "http://10.0.0.202:8001/consumers/ab7fcd95-fa06-456a-9e4a-b2b701a510de/oauth2"
响应
{
"consumer_id": "ab7fcd95-fa06-456a-9e4a-b2b701a510de",
"client_id": "309e682d142147c29f49a83b5498f164",
"id": "2523187c-d4a3-4c7d-b35c-2663b2a02822",
"created_at": 1466928527000,
"redirect_uri": "[\"http:\\/\\/some-domain\\/endpoint\\/\"]",
"name": "openapidoc",
"client_secret": "2d20985e78424ea5922589db71632769"
}
五.为一个client已经登录成功的用户生成auth的access_token
这一步的前面还有一些别的步骤,比如认证系统的用户登录认证,就省略了,直接到生成access_token这一步.
请求
curl -X POST -H "Cache-Control: no-cache" -H "Postman-Token: 8718a5a5-2ff2-bc3f-7e57-70443e86620b" -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "client_id=309e682d142147c29f49a83b5498f164" -F "client_secret=2d20985e78424ea5922589db71632769" -F "scope=phone" -F "provision_key=758017a57c4447628546d0d266f92a9b" -F "authenticated_userid=1" -F "username=vincent" -F "password=123456" -F "grant_type=password" "https://10.0.0.202:8443/docs/oauth2/token"
注意,如果有别的oauth认证方式打开,这一步一定要加grant_type,我一开始也没注意一直不成功,看了源码才知道
响应
{
"refresh_token": "5ec407d2cfcb42639b642355a916e117",
"token_type": "bearer",
"access_token": "36651445140a47329e6d0badf14e09aa",
"expires_in": 720000
}
六.用token来测试访问原有的api
注意,如果是用postman访问,最好先用https在浏览器上访问以下,并忽略未认证证书的警告再来测试.
请求
curl -X GET -H "Authorization: bearer 36651445140a47329e6d0badf14e09aa" "https://10.0.0.202:8443/docs/online_docs/linux/rsync/rsync.html"
成功返回,表示配置成功. 把token换成错误的会得到以下提示.
{“error_description”:”The access token is invalid or has expired”,”error”:”invalid_token”}
如果用http访问,则会得到以下提示.
{“error_description”:”The access token is missing”,”error”:”invalid_request”}
至此,一个授权流程和访问api已经结束.