目前正在使用 asp.net core 2.0 (主要是 web api) 做一个项目, 其中一部分功能需要使用 js 客户端调用 python 的 pandas, 所以需要建立一个 python 的 rest api, 我暂时选用了 hug, 官网在这: http://www.hug.rest/ .
目前项目使用的是 identity server 4, 还有一些 web api 和 js client.
项目的早期后台源码: https://github.com/solenovex/asp.net-core-2.0-web-api-boilerplate
下面开始配置 identity server 4, 我使用的是 windows.
添加 ApiResource:
在 authorization server 项目中的配置文件添加红色部分, 这部分就是 python hug 的 api:
修改 js Client 的配置:
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource(SalesApiSettings.ApiName, SalesApiSettings.ApiDisplayName) {
UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.PreferredUserName, JwtClaimTypes.Email }
},
new ApiResource("purchaseapi", "采购和原料库API") {
UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.PreferredUserName, JwtClaimTypes.Email }
},
new ApiResource("hugapi", "Hug API") {
UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.PreferredUserName, JwtClaimTypes.Email }
}
};
}
修改 js 客户端的 oidc client 配置选项:
// Sales JavaScript Client
new Client
{
ClientId = SalesApiSettings.ClientId,
ClientName = SalesApiSettings.ClientName,
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
AccessTokenLifetime = 60 * 10,
AllowOfflineAccess = true,
RedirectUris = { $"{Startup.Configuration["MLH:SalesApi:ClientBase"]}/login-callback", $"{Startup.Configuration["MLH:SalesApi:ClientBase"]}/silent-renew.html" },
PostLogoutRedirectUris = { Startup.Configuration["MLH:SalesApi:ClientBase"] },
AllowedCorsOrigins = { Startup.Configuration["MLH:SalesApi:ClientBase"] },
AlwaysIncludeUserClaimsInIdToken = true,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
SalesApiSettings.ApiName,
"hugapi"
}
}
添加 hugapi, 与 authorization server 配置对应.
建立 Python Hug api
{
authority: 'http://localhost:5000',
client_id: 'sales',
redirect_uri: 'http://localhost:4200/login-callback',
response_type: 'id_token token',
scope: 'openid profile salesapi hugapi email',
post_logout_redirect_uri: 'http://localhost:4200',
silent_redirect_uri: 'http://localhost:4200/silent-renew.html',
automaticSilentRenew: true,
accessTokenExpiringNotificationTime: 4,
// silentRequestTimeout:10000,
userStore: new WebStorageStateStore({ store: window.localStorage })
}
(可选) 安装 virtualenv:
pip install virtualenv
然后在某个地方建立一个目录:
mkdir hugapi && cd hugapi
建立虚拟环境:
virtualenv venv
激活虚拟环境:
venv\Scripts\activate
然后大约这样显示:
安装 hug:
pip install hug
这时, 参考一下 hug 的文档. 然后建立一个简单的 api. 建立文件 main.py:
运行:
import hug
@hug.get('/home')
def root():
return 'Welcome home!'
hug -f main.py
结果好用:
然后还需要安装这些:
pip install cryptography pyjwt hug_middleware_cors
其中 pyjwt 是一个可以 encode 和 decode JWT 的库, 如果使用 RS256 算法的话, 还需要安装 cryptography.
而 hug_middleware_cors 是 hug 的一个跨域访问中间件 (因为 js 客户端和这个 api 不是在同一个域名下).
添加需要的引用:
然后正确的做法是通过 Authorization Server 的 discovery endpoint 来找到 jwks_uri,
import hug
import jwt
import json
import urllib.request
from jwt.algorithms import get_default_algorithms
from hug_middleware_cors import CORSMiddleware
identity server 4 的 discovery endpoint 的地址是:
http://localhost:5000/.well-known/openid-configuration , 里面能找到各种节点和信息:
但我还是直接写死这个 jwks_uri 吧:
identity server 4 的 jwks_uri, 里面是 public key, 它的结构是这样的:
response = urllib.request.urlopen('http://localhost:5000/.well-known/openid-configuration/jwks')
still_json = json.dumps(json.loads(response.read())['keys'][0])
而我使用 jwt 库, 的参数只能传入一个证书的 json, 也可就是 keys[0].
所以上面的最后一行代码显得有点.......
如果使用 python-jose 这个库会更简单一些, 但是在我 windows 电脑上总是安装失败, 所以还是凑合用 pyjwt 吧.
然后让 hug api 使用 cors 中间件:
然后是 hug 的 authentication 部分:
api = hug.API(__name__)
api.http.add_middleware(CORSMiddleware(api))
通过 rsa.from_jwk(json) 就会得到 key (certificate), 然后通过 jwt.decode 方法可以把 token 进行验证并 decode, 算法是 RS256, 这个方法要求如果 token 里面包含了 aud, 那么方法就需要要指定 audience, 也就是 hugapi.
def token_verify(token):
token = token.replace('Bearer ', '')
rsa = get_default_algorithms()['RS256']
cert = rsa.from_jwk(still_json)
try:
result = jwt.decode(token, cert, algorithms=['RS256'], audience='hugapi')
print(result)
return result
except jwt.DecodeError:
return False
token_key_authentication = hug.authentication.token(token_verify)
最后修改 api 方法, 加上验证:
最后运行 hug api:
@hug.get('/home', requires=token_key_authentication)
def root():
return 'Welcome home!'
hug
-f
main.py
端口应该是 8000.运行 js 客户端, 登陆, 并调用这个 hug api http://localhost:8000/home:
(我的 js 客户端是 angular5 的, 这个没法开源, 公司财产, 不过配置 oidc-client 还是很简单的, 使用)
返回 200, 内容是:
看一下 hug 的 log:
token 被正确验证并解析了. 所以可以进入 root 方法了.
其他的 python api 框架, 都是同样的道理.
可以使用这个例子自行搭建 https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts/7_JavaScriptClient
官方还有一个 nodejs api 的例子: https://github.com/lyphtec/idsvr4-node-jwks
来源: https://www.cnblogs.com/cgzl/p/8270677.html