当前位置: 首页 > 知识库问答 >
问题:

Python中的SAML 2.0服务提供商

蒙勇
2023-03-14

我希望用Python实现一个基于SAML 2.0的服务提供者。

我的Web应用程序目前都是Flask应用程序。我计划制作一个Flask蓝图/装饰器,允许我将单点登录功能放入预先存在的应用程序中。

我已经深入研究了python saml,但不幸的是,存在一些不值得解决的依赖性问题,因为我有太多预先存在的服务器/应用程序,这些服务器/应用程序的环境将不兼容。

PySAML2看起来可以工作,但是几乎没有文档,我很难理解可用的文档。烧瓶应用程序中没有使用PySAML2的示例。

我的身份提供者是Okta。我设置了Okta,以便在Okta登录后,我被重定向到我的应用程序。

有谁能提供关于使用PySAML2的建议,或者关于如何最好地验证正在访问我的应用程序的使用SAML 2.0的用户的建议吗?

共有1个答案

谭建章
2023-03-14

更新:关于在Okta中使用PySAML2的详细说明现已developer.okta.com.

下面是一些在Python/Flask中实现SAML SP的示例代码。此示例代码演示了以下几点:

  1. 支持多个IdP。
  2. 使用Flask登录进行用户管理。
  3. 使用“SSO URL”作为受众限制(以简化IdP上的配置)。
  4. 及时配置用户(“SAML JIT”)
  5. 在属性语句中传递其他用户信息。

没有演示的是执行SP启动的身份验证请求—稍后我将继续介绍。

在某个时候,我希望围绕pysaml2创建一个具有固执己见的默认值的包装器。

最后,与python-saml一样,pysaml2库使用xmlsec1二进制文件。这也可能导致您的服务器环境中的依赖问题。如果是这种情况,您需要考虑用signxml库替换xmlsec1

以下示例中的所有内容都应使用以下设置:

$ virtualenv venv
$ source venv/bin/activate
$ pip install flask flask-login pysaml2

最后,你需要对Okta这边的事情做些什么才能让它发挥作用。

第二:在Okta应用程序配置的Single Sign-On选项卡中,获取url并将其放入名为example的文件中。okta。通用域名格式。元数据。您可以使用下面这样的命令来执行此操作。

$ curl [the metadata url for your Okta application] > example.okta.com.metadata

以下是您的Python/Flask应用程序处理IdP发起的SAMLhtml" target="_blank">请求所需的内容:

# -*- coding: utf-8 -*-
import base64
import logging
import os
import urllib
import uuid
import zlib

from flask import Flask
from flask import redirect
from flask import request
from flask import url_for
from flask.ext.login import LoginManager
from flask.ext.login import UserMixin
from flask.ext.login import current_user
from flask.ext.login import login_required
from flask.ext.login import login_user
from saml2 import BINDING_HTTP_POST
from saml2 import BINDING_HTTP_REDIRECT
from saml2 import entity
from saml2.client import Saml2Client
from saml2.config import Config as Saml2Config

# PER APPLICATION configuration settings.
# Each SAML service that you support will have different values here.
idp_settings = {
    u'example.okta.com': {
        u"metadata": {
            "local": [u'./example.okta.com.metadata']
        }
    },
}
app = Flask(__name__)
app.secret_key = str(uuid.uuid4())  # Replace with your secret key
login_manager = LoginManager()
login_manager.setup_app(app)
logging.basicConfig(level=logging.DEBUG)
# Replace this with your own user store
user_store = {}


class User(UserMixin):
    def __init__(self, user_id):
        user = {}
        self.id = None
        self.first_name = None
        self.last_name = None
        try:
            user = user_store[user_id]
            self.id = unicode(user_id)
            self.first_name = user['first_name']
            self.last_name = user['last_name']
        except:
            pass


@login_manager.user_loader
def load_user(user_id):
    return User(user_id)


@app.route("/")
def main_page():
    return "Hello"


@app.route("/saml/sso/<idp_name>", methods=['POST'])
def idp_initiated(idp_name):
    settings = idp_settings[idp_name]
    settings['service'] = {
        'sp': {
            'endpoints': {
                'assertion_consumer_service': [
                    (request.url, BINDING_HTTP_REDIRECT),
                    (request.url, BINDING_HTTP_POST)
                ],
            },
            # Don't verify that the incoming requests originate from us via
            # the built-in cache for authn request ids in pysaml2
            'allow_unsolicited': True,
            'authn_requests_signed': False,
            'logout_requests_signed': True,
            'want_assertions_signed': True,
            'want_response_signed': False,
        },
    }

    spConfig = Saml2Config()
    spConfig.load(settings)
    spConfig.allow_unknown_attributes = True

    cli = Saml2Client(config=spConfig)
    try:
        authn_response = cli.parse_authn_request_response(
            request.form['SAMLResponse'],
            entity.BINDING_HTTP_POST)
        authn_response.get_identity()
        user_info = authn_response.get_subject()
        username = user_info.text
        valid = True
    except Exception as e:
        logging.error(e)
        valid = False
        return str(e), 401

    # "JIT provisioning"
    if username not in user_store:
        user_store[username] = {
            'first_name': authn_response.ava['FirstName'][0],
            'last_name': authn_response.ava['LastName'][0],
            }
    user = User(username)
    login_user(user)
    # TODO: If it exists, redirect to request.form['RelayState']
    return redirect(url_for('user'))


@app.route("/user")
@login_required
def user():
    msg = u"Hello {user.first_name} {user.last_name}".format(user=current_user)
    return msg


if __name__ == "__main__":
    port = int(os.environ.get('PORT', 5000))
    if port == 5000:
        app.debug = True
    app.run(host='0.0.0.0', port=port)
 类似资料:
  • 我必须在具有OpenSAML2.5.1(旧版本的cas)的代码库上实现SAML2.0,不幸的是,我无法使用spring saml安全扩展(它使用了新版本的opensaml)。我已经研究了spring扩展如何处理授权请求/响应,并在一定程度上对其进行了反向工程。我能够从sp呼叫idp(sso圈)并尝试登录,但在提交时出现500个错误:原因:无法进行单点登录或联合。 是否有任何示例java opens

  • 简介 服务提供者是所有 Lumen 应用程序启动的中心所在。包括你自己的应用程序,以及所有的 Lumen 核心服务,都是通过服务提供者启动的。 但是,我们所说的「启动」指的是什么?一般而言,我们指的是 注册 事物,包括注册服务容器绑定、事件侦听器、中间件,甚至路由。服务提供者是设置你的应用程序的中心所在。 若你打开 Lumen 的 bootstrap/app.php 文件,你将会看到 $app->

  • 服务提供者是组件和CatLib联系的桥梁。同时也是CatLib启动的中心,所有的服务都是通过服务提供者定义的。 名词定义 组件 组件与CatLib没有任何关系,她们可以独立的运行在不同的框架中。 服务 是由服务提供者将由一个或者多个组件组合而成,并提供一组可以被开发者使用的接口。 容器 CatLib 依赖注入容器。 架构图 创建服务提供者 服务提供者是用来描述一个服务如何为使用者提供服务的,这些关

  • 我在Novell iManager中收到错误消息“XML格式错误。CVC-complex-type.2.4.A:发现以元素'md:encryptionMethod'开始的无效内容。需要”{“http://www.w3.org/2000/09/xmldsig#”:keyinfo}“之一。”添加此服务提供程序元数据时: 我在Novell iManager中所做的事情:IdentityServers->

  • 简介 服务提供器是所有 Laravel 应用程序引导中心。你的应用程序以及 Laravel 的所有核心服务都是通过服务提供器进行引导。 在这里,我们说的「引导」其实是指 注册 ,比如注册服务容器绑定、事件监听器、中间件,甚至是路由的注册。服务提供器是配置你的应用程序的中心。 Laravel 的 config/app.php 文件中有一个 providers 数组。数组中的内容是应用程序要加载的所有

  • 我正在我的服务中添加SSO功能,以允许客户使用他们的广告帐户登录。为此,我使用componentpro中的SAML组件。com执行安全交互的正确方法是什么: < li >从IdP方面:除了IdP url,客户还应该向我的服务提供什么?任何只包含公钥的证书? < li >从服务提供商的角度:我应该向客户提供什么? < li >选择的SAML工具使用证书对发送给IdP的SAML请求进行签名,并对来自I