基于kong + oauth2 + acl的用户接入权限管理

需求说明

对admin进行分组管理,不同的用户有访问不同api(服务)的权限,类似django admin的用户组功能

由于认证系统是完全可信的内部系统,简单起见使用密码授权方式

在网关层做接入权限管控,而非后端应用的业务权限

启动kong

# 启动kong使用的数据库postgres/cassandra
docker run -d --name kong-database \
        -p 5432:5432 \
        -e "POSTGRES_USER=kong" \
        -e "POSTGRES_DB=kong" \
        postgres:9.6

# kong配置数据初始化
docker run --rm \
  --link kong-database:kong-database \
  -e "KONG_DATABASE=postgres" \
  -e "KONG_PG_HOST=kong-database" \
  kong:0.14.0 kong migrations up

# 启动kong容器
docker run -d --name kong \
  --link kong-database:kong-database \
  -e "KONG_DATABASE=postgres" \
  -e "KONG_PG_HOST=kong-database" \
  -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
  -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
  -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
  -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
  -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
  -p 8000:8000 \
  -p 8443:8443 \
  -p 8001:8001 \
  -p 8444:8444 \
  kong:0.14.0

实现

服务和权限一对一关系,每一个服务或者api拥有唯一的权限名,不同的服务或api不同的权限, 通过对用户赋予不同的权限,从而限制用户访问指定的api或服务

OAuth2密码模式: 用户认证
基于kong + oauth2 + acl的用户接入权限管理

acl白名单: 用户分组权限

ref:https://docs.konghq.com/hub/kong-inc/oauth2/
ref:https://docs.konghq.com/hub/kong-inc/acl/

整体流程

基于kong + oauth2 + acl的用户接入权限管理

用户认证服务器在Kong的内侧,我们需要通过Kong进行访问

  • 用户发送账号密码到 Kong服务器的用户验证API;
  • Kong服务器将请求1转发给用户验证服务器;
  • 用户认证服务器验证用户账号密码是否正确,如果不正确直接返回错误结果并完成用户认证;如果正确则将:用户账号密码、client_id和client_secret、scope、provision_key、用户ID 发送给 Kong 的OAuth2 接口
  • Kong生成access_token并返回给用户认证服务器,并缓存与过期时间相同的 scope,用户ID
  • 认证服务器将4中收到的access_token返回请求2给Kong,Kong再返回请求1给用户。
  • 用户得到access_token之后,就可以访问有OAuth2验证的接口了。access_token中已隐含了用户ID和scope。

创建两个api(或者服务), 分别为g 和 f

g服务:https://api.github.com/feeds(github api接口口)
f服务:http://mockbin.com/request (http请求信息回显接口)

网关转发策略(Request path),通过请求路径管理不同服务

http://localhost:8000/g/        =>           https://api.github.com/feeds

http://localhost:8000/f/        =>           http://mockbin.com/request

1. 创建api g,添加oauth2插件, 添加acl插件(设置白名单组)

创建api g
curl -i -X POST \
  --url http://localhost:8001/apis/ \
  --data 'name=g' \
  --data 'uris=/g' \
  --data 'upstream_url=https://api.github.com/feeds'
添加oauth2插件
curl -X POST http://localhost:8001/apis/g/plugins \
  --data "name=oauth2" \
  --data "config.enable_password_grant=true" \
  --data "config.scopes=email,phone,address" \
  --data "config.mandatory_scope=true"\
  --data "config.global_credentials=true"

开启global_credentials全局凭证,否则多个api不能共用token

=>

{"created_at":1539681371000,"config":{"refresh_token_ttl":1209600,"scopes":["email","phone","address"],"mandatory_scope":true,
"provision_key":"bHR6ABHpSCAXbIIdLznfNco21WTa3Anb","hide_credentials":false,"enable_authorization_code":true,
"enable_implicit_grant":false,"global_credentials":false,"accept_http_if_already_terminated":false,
"enable_password_grant":false,"enable_client_credentials":false,"anonymous":"","token_expiration":7200,"auth_header_name":"authorization"},
"id":"5f820751-d937-4bcf-94e6-e233504e4c58","name":"oauth2","api_id":"f24c8181-bff4-4b19-8b9f-0c256a86c157","enabled":true}

记录下g服务的

provision_key: wBBr78rYxuN9jOo3VTQXrFYtG2tZLN8J

添加acl插件,设置分组(白名单)read
curl -X POST http://localhost:8001/apis/g/plugins \
  --data "name=acl" \
  --data "config.whitelist=read" 
  # --data "config.hide_groups_header=true"

g服务需要 read权限

2. 创建api f,添加oauth2插件, 添加acl插件(设置白名单组)

创建api f
curl -i -X POST \
  --url http://localhost:8001/apis/ \
  --data 'name=f' \
  --data 'uris=/f' \
  --data 'upstream_url=http://mockbin.com/request'
添加oauth2插件
curl -X POST http://localhost:8001/apis/f/plugins \
  --data "name=oauth2" \
  --data "config.enable_password_grant=true" \
  --data "config.scopes=email,phone,address" \
  --data "config.mandatory_scope=true" \
  --data "config.global_credentials=true"

=>

{"created_at":1539681457000,"config":{"refresh_token_ttl":1209600,"scopes":["email","phone","address"],"mandatory_scope":true,
"provision_key":"wBBr78rYxuN9jOo3VTQXrFYtG2tZLN8J","hide_credentials":false,"enable_authorization_code":true,
"enable_implicit_grant":false,"global_credentials":false,"accept_http_if_already_terminated":false,"enable_password_grant":false,
"enable_client_credentials":false,"anonymous":"","token_expiration":7200,"auth_header_name":"authorization"},
"id":"8680d0e6-3f29-41f5-a0bc-7ceff6cb4f4f","name":"oauth2","api_id":"05e03a02-770c-474d-81e7-40344fa0f469","enabled":true}

记录下f服务的

provision_key: wBBr78rYxuN9jOo3VTQXrFYtG2tZLN8J

后面获取token要用

添加acl插件,设置分组(白名单)write
curl -X POST http://localhost:8001/apis/f/plugins \
  --data "name=acl" \
  --data "config.whitelist=write" 
  # --data "config.hide_groups_header=true"

f服务需要 write权限

3. 创建comsumer

ref: https://blog.****.net/weixin_33836223/article/details/86849500

用户名: user123

curl -X POST http://localhost:8001/consumers/ \
  --data "username=user123"

=>

{"custom_id":null,"created_at":1539681490,"username":"user123","id":"1317f755-eb65-46ea-a792-825d49869ec7"}

创建应用

curl -X POST http://localhost:8001/consumers/user123/oauth2 \
  --data "name=test-app" \
  --data "redirect_uri=http://test.my"

=>

{"client_id":"m0gtiqvlg8Mb08xDC2ZoQDUqHjzdjVnK","created_at":1539681543000,"id":"72910e81-3fd0-4c99-9eca-15a29ebc0ce9","redirect_uri":["http:\/\/test.my"],
"name":"test-app","client_secret":"N7R1HL9rgLdELlCJ1W6HSEEemzu0bb08","consumer_id":"1317f755-eb65-46ea-a792-825d49869ec7"}

记录下
client_id: m0gtiqvlg8Mb08xDC2ZoQDUqHjzdjVnK
client_secret: N7R1HL9rgLdELlCJ1W6HSEEemzu0bb08

4. 给consumer关联相应的组,可关联多个组

curl -X POST http://localhost:8001/consumers/user123/acls \
  --data "group=write"

给用户user123赋予 write权限
此时用户user123应该可以访问f服务而不能访问g服务(g: read, f:wirte)

5. 获取token

选择任意一个服务(g/f)获取token

curl -k \
  --data "client_id=m0gtiqvlg8Mb08xDC2ZoQDUqHjzdjVnK" \
  --data "client_secret=N7R1HL9rgLdELlCJ1W6HSEEemzu0bb08" \
  --data "provision_key=same" \
  --data "scope=email" \
  --data "authenticated_userid=b48bf407-c2b7-41a9-8e0f-43eead2fc60f" \
  --data "grant_type=password" \
  https://localhost:8443/f/oauth2/token

authenticated_userid为应用系统用户标识(比如用户id或者uuid)

=>

{"refresh_token":"pWAhNfCdJ78JN9qU1xiLCwXrOyjLqopo","token_type":"bearer","access_token":"pwA2qEQLQQmpatG4NOBE8SGB670UflH1","expires_in":7200}

6.访问api

访问api f

curl -i -k -X GET --url http://localhost:8000/f/?access_token=KzDR0BxQrEhQTmI2dmSphnZc5dxR6jAK

访问api g

curl -i -k -X GET --url http://localhost:8000/g/?access_token=KzDR0BxQrEhQTmI2dmSphnZc5dxR6jAK

=>

HTTP/1.1 403 Forbidden
Date: Wed, 17 Oct 2018 02:08:58 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Server: kong/0.14.1
Content-Length: 46

{"message":"You cannot consume this service"}

g服务需要 read权限
f服务需要 write权限

由于用户user123没有和read组管理,故不能访问g的api

关联user123和read组

curl -X POST http://localhost:8001/consumers/user123/acls \
  --data "group=read"

再次访问api g成功

curl -i -k -X GET --url http://localhost:8000/g/?access_token=KzDR0BxQrEhQTmI2dmSphnZc5dxR6jAK

从而实现基于acl白名单方式的用户接入权限管理

上一篇:阶段5 3.微服务项目【学成在线】_day16 Spring Security Oauth2_15-认证接口开发-Redis配置


下一篇:阶段5 3.微服务项目【学成在线】_day16 Spring Security Oauth2_07-SpringSecurityOauth2研究-Oauth2授权码模式-资源服务授权测试