用 Identity Server 4 (JWKS 端点和 RS256 算法) 来保护 Python web api

简介: [新添加] 本文对应的源码 (多个flow, clients, 调用python api): https://github.com/solenovex/Identity-Server-4-Python-Hug-Api-Jwks 目前正在使用asp.

[新添加] 本文对应的源码 (多个flow, clients, 调用python api): https://github.com/solenovex/Identity-Server-4-Python-Hug-Api-Jwks

目前正在使用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:

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 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"
                    }
                }

修改js客户端的oidc client配置选项:

添加 hugapi, 与authorization server配置对应.

{
        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 })
    }

建立Python Hug api

(可选) 安装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不是在同一个域名下).

添加需要的引用:

import hug
import jwt
import json
import urllib.request
from jwt.algorithms import get_default_algorithms
from hug_middleware_cors import CORSMiddleware

然后正确的做法是通过Authorization Server的discovery endpoint来找到jwks_uri,

identity server 4 的discovery endpoint的地址是:

http://localhost:5000/.well-known/openid-configuration, 里面能找到各种节点和信息:

 

但我还是直接写死这个jwks_uri吧:

response = urllib.request.urlopen('http://localhost:5000/.well-known/openid-configuration/jwks')
still_json = json.dumps(json.loads(response.read())['keys'][0])

identity server 4的jwks_uri, 里面是public key, 它的结构是这样的:

而我使用jwt库, 的参数只能传入一个证书的json, 也可就是keys[0].

所以上面的最后一行代码显得有点.......

如果使用python-jose这个库会更简单一些, 但是在我windows电脑上总是安装失败, 所以还是凑合用pyjwt吧.

然后让hug api使用cors中间件:

api = hug.API(__name__)
api.http.add_middleware(CORSMiddleware(api))

然后是hug的authentication部分:

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)

通过rsa.from_jwk(json) 就会得到key (certificate), 然后通过jwt.decode方法可以把token进行验证并decode, 算法是RS256, 这个方法要求如果token里面包含了aud, 那么方法就需要要指定audience, 也就是hugapi.

最后修改api 方法, 加上验证:

@hug.get('/home', requires=token_key_authentication)
def root():
    return 'Welcome home!'

最后运行 hug api:

hug -f main.py

端口应该是8000.

运行js客户端,登陆, 并调用这个hug api http://localhost:8000/home:

(我的js客户端是angular5的, 这个没法开源, 公司财产, 不过配置oidc-client还是很简单的, 使用)

返回200, 内容是: 

看一下hug的log:

token被正确验证并解析了. 所以可以进入root方法了.

 
其他的python api框架, 都是同样的道理.

[新添加] 本文对应的源码 (多个flow, clients, 调用python api): https://github.com/solenovex/Identity-Server-4-Python-Hug-Api-Jwks

可以使用这个例子自行搭建 https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts/7_JavaScriptClient 

官方还有一个nodejs api的例子: https://github.com/lyphtec/idsvr4-node-jwks

今日修改后的代码: 

import json
import hug
import jwt
import requests
from jwt.algorithms import get_default_algorithms
from hug_middleware_cors import CORSMiddleware

api = hug.API(__name__)
api.http.add_middleware(CORSMiddleware(api))


def token_verify(token):
    access_token = token.replace('Bearer ', '')
    token_header = jwt.get_unverified_header(access_token)
    res = requests.get(
        'http://localhost:5000/.well-known/openid-configuration')
    jwk_uri = res.json()['jwks_uri']
    res = requests.get(jwk_uri)
    jwk_keys = res.json()

    rsa = get_default_algorithms()['RS256']
    key = json.dumps(jwk_keys['keys'][0])
    public_key = rsa.from_jwk(key)

    try:
        result = jwt.decode(access_token, public_key, algorithms=[
                            token_header['alg']], audience='api1')
        return result
    except jwt.DecodeError:
        return False


token_key_authentication = hug.authentication.token(token_verify)


@hug.get('/identity', requires=token_key_authentication)
def root(user: hug.directives.user):
    print(user)
    return user

 我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan

下面是我的关于ASP.NET Core Web API相关技术的公众号--草根专栏:

目录
相关文章
|
20天前
|
JSON 数据可视化 API
Python 中调用 DeepSeek-R1 API的方法介绍,图文教程
本教程详细介绍了如何使用 Python 调用 DeepSeek 的 R1 大模型 API,适合编程新手。首先登录 DeepSeek 控制台获取 API Key,安装 Python 和 requests 库后,编写基础调用代码并运行。文末包含常见问题解答和更简单的可视化调用方法,建议收藏备用。 原文链接:[如何使用 Python 调用 DeepSeek-R1 API?](https://apifox.com/apiskills/how-to-call-the-deepseek-r1-api-using-python/)
|
10天前
|
数据采集 Web App开发 存储
打造高效的Web Scraper:Python与Selenium的完美结合
本文介绍如何使用Python结合Selenium,通过代理IP、设置Cookie和User-Agent抓取BOSS直聘的招聘信息,包括公司名称、岗位、要求和薪资。这些数据可用于行业趋势、人才需求、企业动态及区域经济分析,为求职者、企业和分析师提供宝贵信息。文中详细说明了环境准备、代理配置、登录操作及数据抓取步骤,并提醒注意反爬虫机制和验证码处理等问题。
打造高效的Web Scraper:Python与Selenium的完美结合
|
2天前
|
数据采集 供应链 API
实战指南:通过1688开放平台API获取商品详情数据(附Python代码及避坑指南)
1688作为国内最大的B2B供应链平台,其API为企业提供合法合规的JSON数据源,直接获取批发价、SKU库存等核心数据。相比爬虫方案,官方API避免了反爬严格、数据缺失和法律风险等问题。企业接入1688商品API需完成资质认证、创建应用、签名机制解析及调用接口四步。应用场景包括智能采购系统、供应商评估模型和跨境选品分析。提供高频问题解决方案及安全合规实践,确保数据安全与合法使用。立即访问1688开放平台,解锁B2B数据宝藏!
|
1月前
|
数据采集 供应链 API
Python爬虫与1688图片搜索API接口:深度解析与显著收益
在电子商务领域,数据是驱动业务决策的核心。阿里巴巴旗下的1688平台作为全球领先的B2B市场,提供了丰富的API接口,特别是图片搜索API(`item_search_img`),允许开发者通过上传图片搜索相似商品。本文介绍如何结合Python爬虫技术高效利用该接口,提升搜索效率和用户体验,助力企业实现自动化商品搜索、库存管理优化、竞品监控与定价策略调整等,显著提高运营效率和市场竞争力。
101 3
|
2月前
|
JavaScript API C#
【Azure Developer】Python代码调用Graph API将外部用户添加到组,结果无效,也无错误信息
根据Graph API文档,在单个请求中将多个成员添加到组时,Python代码示例中的`members@odata.bind`被错误写为`members@odata_bind`,导致用户未成功添加。
56 10
|
2月前
|
数据采集 JSON API
如何利用Python爬虫淘宝商品详情高级版(item_get_pro)API接口及返回值解析说明
本文介绍了如何利用Python爬虫技术调用淘宝商品详情高级版API接口(item_get_pro),获取商品的详细信息,包括标题、价格、销量等。文章涵盖了环境准备、API权限申请、请求构建和返回值解析等内容,强调了数据获取的合规性和安全性。
|
1月前
|
数据采集 JavaScript 前端开发
京东商品详情 API 接口指南(Python 篇)
本简介介绍如何使用Python抓取京东商品详情数据。首先,需搭建开发环境并安装必要的库(如requests、BeautifulSoup和lxml),了解京东反爬虫机制,确定商品ID获取方式。通过发送HTTP请求并解析HTML,可提取价格、优惠券、视频链接等信息。此方法适用于电商数据分析、竞品分析、购物助手及内容创作等场景,帮助用户做出更明智的购买决策,优化营销策略。
|
2月前
|
存储 API 数据库
使用Python开发获取商品销量详情API接口
本文介绍了使用Python开发获取商品销量详情的API接口方法,涵盖API接口概述、技术选型(Flask与FastAPI)、环境准备、API接口创建及调用淘宝开放平台API等内容。通过示例代码,详细说明了如何构建和调用API,以及开发过程中需要注意的事项,如数据库连接、API权限、错误处理、安全性和性能优化等。
180 5
|
2月前
|
API Python
【Azure Developer】分享一段Python代码调用Graph API创建用户的示例
分享一段Python代码调用Graph API创建用户的示例
74 11
|
2月前
|
JSON 安全 API
Python调用API接口的方法
Python调用API接口的方法
461 5

热门文章

最新文章