zoukankan      html  css  js  c++  java
  • 用 Identity Server 4 (JWKS 端点和 RS256 算法) 来保护 Python web api

    [新添加] 本文对应的源码 (多个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

    激活虚拟环境:

    venvScriptsactivate

    然后大约这样显示:

    安装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

  • 相关阅读:
    初步认识,合并集(树)
    20180918-1 词频统计
    20181011-1 每周例行报告
    2018091-2 博客作业
    项目第六天
    项目第五天
    项目第四天
    项目第三天
    总结随笔
    测试报告
  • 原文地址:https://www.cnblogs.com/cgzl/p/8270677.html
Copyright © 2011-2022 走看看