跳转到主要内容

使用对称密钥算法对数据进行签名和验证(字典、字符串)。

项目描述

允许您使用对称密钥算法加密轻松地对数据进行签名。允许您验证已签名的数据并识别可能的验证错误。使用sha-(1, 224, 256, 385和512)/hmac进行签名加密。允许使用自定义哈希算法。提供用于签名(和验证)字典和URL的快捷函数。

PyPI Version Supported Python versions Build Status Documentation Status GPL-2.0-only OR LGPL-2.1-or-later Coverage

关键概念

相互通信的主机共享密钥,该密钥用于签名数据(请求)。密钥永远不会被发送。

以下是一个案例:HTTP请求的签名。每个(HTTP)请求在发送端使用共享密钥签名,并产生三元组(签名认证用户有效期至),用于签名请求。

  • 签名 (str):生成的签名。

  • 认证用户 (str):发起请求的用户。可以是任何内容。

  • 有效期至 (floatstr):签名的过期时间(Unix时间戳)。

在接收端,使用共享密钥验证(HTTP请求)数据。检查签名是否有效且未过期。

┌─────────────┐           Data              ┌─────────────┐
│   Host 1    ├────────────────────────────>│   Host 2    │
│ ─────────── │                             │ ─────────── │
│ secret key  │                             │ secret key  │
│ 'my-secret' │<────────────────────────────┤ 'my-secret' │
└─────────────┘           Data              └─────────────┘

功能

核心 ska 模块

  • 签名字典。

  • 验证签名字典。

  • 签名URL。添加并签名额外的URL数据。

  • 验证URL。

  • 使用内置算法之一(HMAC SHA-1、HMAC SHA-224、HMAC SHA-256、HMAC SHA-384或HMAC SHA-512)或定义一个自定义算法。

Django ska 模块 (ska.contrib.django.ska)

  • 用于签名绝对URL的模型装饰器。仅对授权方保护视图(不需要认证)的视图(包括基于类的视图)装饰器。

  • 基于使用 ska 生成的签名(令牌)的 Django 认证后端,允许您实现无密码登录 Django 网站。支持多个密钥(每个提供者)。包含方便的回调(可以按提供者进行自定义),用于各种认证状态。

  • 模板标签,用于在模板中签名URL。

  • django-constance 集成(用于无密码认证)。

  • Django REST Framework 集成(用于保护 ViewSets,获取 JWT 令牌进行认证)。

先决条件

当前

  • 核心 ska 模块需要 Python 3.8、3.9、3.10 和 3.11。

  • Django ska 模块 (ska.contrib.django.ska) 需要上述内容加上 Django 3.2、4.1 或 4.2。此外,还需要某些版本的 django-constancedjangorestframework。具体的版本要求主要取决于使用的 Django 版本。查看 示例要求,以了解与特定 Django 版本一起测试过的 django-constancedjangorestframework 的版本。

过去

  • 在版本 1.10 中宣布将停止支持 Python 3.6 和 3.7。截至 1.9.1,一切仍然正常工作。

  • 在版本 1.8 中宣布将停止支持 Python 2.7 和 3.5。截至 1.7.5,一切仍然正常工作。

  • 在版本 1.6.8 中宣布将停止支持 Python 3.4。截至 1.6.8,一切仍然正常工作。

  • 在版本 1.10 中宣布将停止支持 Django 2.2、3.0、3.1 和 4.0。截至 1.9.1,一切仍然是向后兼容的。

  • 在版本 1.6 中宣布将停止支持 Django 1.5、1.6 和 1.7。截至 1.6,一切仍然是向后兼容的。

  • 在版本 1.6 中宣布将停止支持 Python 2.6 和 3.3。截至 1.6,一切仍然是向后兼容的(在可能的范围内)。

生态系统

需要用于其他语言的 ska 吗?请查看以下关联项目

  • skajs:NodeJS 的 ska 实现(支持 CommonJS 和 ESM,Node >= 14)。

  • skaphp:PHP 的 ska 实现(>= 7.2)。

在 Python、NodeJS 和 PHP 实现之间生成的签名是互兼容的。

安装

来自 PyPI 的最新稳定版本

pip install ska

或来自 GitHub 的最新开发版本。

pip install https://github.com/barseghyanartur/ska/archive/main.tar.gz

使用示例

有关与Django的集成,请参阅Django集成部分。

基本用法

纯Python使用。

发送方

签名URL非常简单,如下所示。

所需的导入。

from ska import sign_url

生成签名URL。

signed_url = sign_url(
    auth_user='user',
    secret_key='your-secret_key',
    url='http://e.com/api/'
)
GET http://e.com/api/?valid_until=1378045287.0&auth_user=user&signature=YlZpLFsjUKBalL4x5trhkeEgqE8%3D

签名默认有效期是10分钟(600秒)。如果您想使用不同的有效期,请向sign_url函数提供lifetime参数。

默认情况下,包含生成的签名值的(GET)参数名称是signature。如果您想使用不同的名称,请向sign_url函数提供signature_param参数。

默认情况下,包含auth_user值的(GET)参数名称是auth_user。如果您想使用不同的名称,请向sign_url函数提供auth_user_param参数。

默认情况下,包含valid_until值的(GET)参数名称是valid_until。如果您想使用不同的名称,请向sign_url函数提供valid_until_param参数。

注意,默认情况下,在给定的url和生成的签名参数之后会添加一个后缀‘?’。如果您想使用自定义后缀,请向sign_url函数提供suffix参数。如果您想去掉后缀,将其值设置为空字符串。

经过所有自定义后,它看起来如下所示

from ska import HMACSHA512Signature  # Use HMAC SHA-512 algorithm

signed_url = sign_url(
    auth_user='user',
    secret_key='your-secret_key',
    lifetime=120,
    url='http://e.com/api/',
    signature_param='signature',
    auth_user_param='auth_user',
    valid_until_param='valid_until',
    signature_cls=HMACSHA512Signature
)

您还可以通过提供extra参数(字典)来向签名中添加额外的数据。注意,附加数据也会被签名。如果请求被篡改(值与最初提供的值不同),签名将变得无效。

sign_url(
    auth_user='user',
    secret_key='your-secret_key',
    url='http://e.com/api/',
    extra={
        'email': 'doe@example.com',
        'last_name': 'Doe',
        'first_name': 'Joe'
    }
)

现在您可以继续使用签名URL进行请求。如果您使用著名的requests库,它将是如下所示。

import requests
requests.get(signed_url)

如果您想使用POST方法,您可能需要获取一个字典,以便稍后将其附加到POST数据中。

所需的导入。

from ska import signature_to_dict

生成包含签名数据的字典,准备将其放入请求(例如POST)数据中。上述所有针对sign_url函数的自定义设置也适用于signature_to_dict

signature_dict = signature_to_dict(
    auth_user='user',
    secret_key='your-secret_key'
)
{
    'signature': 'YlZpLFsjUKBalL4x5trhkeEgqE8=',
    'auth_user': 'user',
    'valid_until': '1378045287.0'
}

向签名添加附加数据的方式相同

signature_dict = signature_to_dict(
    auth_user='user',
    secret_key='your-secret_key',
    extra={
        'email': 'john.doe@mail.example.com',
        'first_name': 'John',
        'last_name': 'Doe'
    }
)
{
    'auth_user': 'user',
    'email': 'john.doe@mail.example.com',
    'extra': 'email,first_name,last_name',
    'first_name': 'John',
    'last_name': 'Doe',
    'signature': 'cnSoU/LnJ/ZhfLtDLzab3a3gkug=',
    'valid_until': 1387616469.0
}

如果您出于某种原因更喜欢更低级别的实现,请阅读高级用法(低级别)章节中的相同部分。

接收方

验证签名请求数据非常简单,如下所示。

所需的导入。

from ska import validate_signed_request_data

验证签名请求数据。注意,data值预期为字典;这里以request.GET为例。它可能会因您的框架而异(除非您使用Django)。

validation_result = validate_signed_request_data(
    data=request.GET,  # Note, that ``request.GET`` is given as example.
    secret_key='your-secret_key'
)

validate_signed_request_data生成一个ska.SignatureValidationResult对象,该对象包含以下数据。

  • resultbool):如果数据有效,则为True。否则为False。

  • reasonlist):字符串列表,指示验证错误。如果result为True,则为空列表。

默认情况下,包含签名值的(GET)参数名称是signature。如果您想使用不同的名称,请向validate_signed_request_data函数提供signature_param参数。

默认情况下,包含auth_user值的(GET)参数名称是auth_user。如果您想使用不同的名称,请向validate_signed_request_data函数提供auth_user_param参数。

默认的(GET)参数名称为valid_until,表示有效期限。如果您想使用不同的名称,请向validate_signed_request_data函数提供valid_until_param参数。

在所有自定义设置下,它将如下所示。注意,这里以request.GET为例。

from ska import HMACSHA256Signature  # Use HMAC SHA-256 algorithm

validation_result = validate_signed_request_data(
    data=request.GET,
    secret_key='your-secret_key',
    signature_param='signature',
    auth_user_param='auth_user',
    valid_until_param='valid_until',
    signature_cls=HMACSHA256Signature
)

如果您出于某种原因更喜欢更低级别的实现,请阅读高级用法(低级别)章节中的相同部分。

命令行使用

可以使用ska.generate_signed_url模块从命令行生成签名URL。

参数:

-h, --help            show this help message and exit

-au AUTH_USER, --auth-user AUTH_USER
                      `auth_user` value

-sk SECRET_KEY, --secret-key SECRET_KEY
                      `secret_key` value

-vu VALID_UNTIL, --valid-until VALID_UNTIL
                      `valid_until` value

-l LIFETIME, --lifetime LIFETIME
                      `lifetime` value

-u URL, --url URL     URL to sign

-sp SIGNATURE_PARAM, --signature-param SIGNATURE_PARAM
                      (GET) param holding the `signature` value

-aup AUTH_USER_PARAM, --auth-user-param AUTH_USER_PARAM
                      (GET) param holding the `auth_user` value

-vup VALID_UNTIL_PARAM, --valid-until-param VALID_UNTIL_PARAM
                      (GET) param holding the `auth_user` value
示例:

ska-sign-url -au user -sk your-secret-key --url http://example.com

高级使用(低级)

发送方

所需的导入。

from ska import Signature, RequestHelper

生成签名。

signature = Signature.generate_signature(
    auth_user='user',
    secret_key='your-secret-key'
)

签名的默认有效期为10分钟(600秒)。如果您想设置不同的有效期,请向generate_signature方法提供lifetime参数。

signature = Signature.generate_signature(
    auth_user='user',
    secret_key='your-secret-key',
    lifetime=120  # Signature lifetime set to 120 seconds.
)

向签名中添加额外的数据与sign_url的方式相同。

signature = Signature.generate_signature(
    auth_user='user',
    secret_key='your-secret-key',
    extra={
        'email': 'doe@example.com',
        'last_name': 'Doe',
        'first_name': 'Joe'
    }
)

对于HMAC SHA-384算法,它将如下所示。

from ska import HMACSHA384Signature

signature = HMACSHA384Signature.generate_signature(
    auth_user='user',
    secret_key='your-secret-key'
)

您的端点使用特定的参数名称,并且您需要将生成的签名参数封装到URL中。为了轻松完成任务,请创建一个请求助手。将(GET)参数的名称输入到请求助手,并让它为您生成一个签名端点URL。

request_helper = RequestHelper(
    signature_param='signature',
    auth_user_param='auth_user',
    valid_until_param='valid_until'
)

将签名参数附加到端点URL。

signed_url = request_helper.signature_to_url(
    signature=signature,
    endpoint_url='http://e.com/api/'
)
GET http://e.com/api/?valid_until=1378045287.0&auth_user=user&signature=YlZpLFsjUKBalL4x5trhkeEgqE8%3D

发送请求。

import requests
r = requests.get(signed_url)

对于HMAC SHA-384算法,它将如下所示。

from ska import HMACSHA384Signature

request_helper = RequestHelper(
    signature_param='signature',
    auth_user_param='auth_user',
    valid_until_param='valid_until',
    signature_cls=HMACSHA384Signature
)

signed_url = request_helper.signature_to_url(
    signature=signature,
    endpoint_url='http://e.com/api/'
)

接收方

所需的导入。

from ska import RequestHelper

创建请求助手。您的端点使用特定的参数名称。为了轻松完成任务,我们将这些参数输入到请求助手,并让它为我们提取签名请求中的数据。

request_helper = RequestHelper(
    signature_param='signature',
    auth_user_param='auth_user',
    valid_until_param='valid_until'
)

验证请求数据。注意,这里以request.GET为例。

validation_result = request_helper.validate_request_data(
    data=request.GET,
    secret_key='your-secret-key'
)

您的实现将取决于您自己,但可能如下所示。

if validation_result.result:
    # Validated, proceed further
    # ...
else:
    # Validation not passed.
    raise Http404(validation_result.reason)

您也可以通过调用ska.Signaturevalidate_signature方法来简单地验证签名。

Signature.validate_signature(
    signature='EBS6ipiqRLa6TY5vxIvZU30FpnM=',
    auth_user='user',
    secret_key='your-secret-key',
    valid_until='1377997396.0'
)

Django集成

ska提供了Django模型-和视图装饰器来生成签名URL和验证端点,以及一个认证后端,允许使用ska生成的签名令牌进行无密码登录到Django网站。还有一个用于签名URL的模板标签。

演示

为了能够快速评估ska,已经创建了一个演示应用程序(带有快速安装程序)(在Ubuntu/Debian上运行,也可能在其他Linux系统上运行,但无法保证)。按照以下说明,在一分钟内运行演示。

获取最新的ska_example_app_installer.sh并执行它

wget -O - https://raw.github.com/barseghyanartur/ska/stable/examples/ska_example_app_installer.sh | bash

打开您的浏览器并测试应用程序。

Foo列表(ska受保护的视图)

认证页面(ska认证后端)

Django管理界面

配置

密钥(str)必须在您的项目中的settings模块中定义。

SKA_SECRET_KEY = 'my-secret-key'

以下变量可以在您的项目中的settings模块中重写。

  • SKA_UNAUTHORISED_REQUEST_ERROR_MESSAGEstr):纯文本错误消息。默认为“未经授权的请求。{0}”。

  • SKA_UNAUTHORISED_REQUEST_ERROR_TEMPLATEstr):在发生401响应时应渲染的401模板的路径。默认为空字符串(未提供)。

  • SKA_AUTH_USERstr):ska.sign_url函数的auth_user参数。默认为“ska-auth-user”。

请参阅工作的示例项目

多个密钥

想象一下,你有一个网站,你想为各种客户/发件人提供无密码登录功能,并且你不想让他们共享一个密钥,而是拥有各自的密钥。此外,你还希望为每个单独的客户/发件人和不同类型的用户认证执行非常定制的回调。

                          ┌────────────────┐
                          │ Site providing │
                          │ authentication │
                          │ ────────────── │
                          │ custom secret  │
                          │    keys per    │
                          │     client     │
                          │ ────────────── │
                          │ Site 1: 'sk-1' │
             ┌───────────>│ Site 2: 'sk-2' │<───────────┐
             │            │ Site 3: 'sk-3' │            │
             │      ┌────>│ Site 4: 'sk-4' │<────┐      │
             │      │     └────────────────┘     │      │
             │      │                            │      │
             │      │                            │      │
┌────────────┴─┐  ┌─┴────────────┐  ┌────────────┴─┐  ┌─┴────────────┐
│    Site 1    │  │    Site 2    │  │    Site 3    │  │    Site 4    │
│ ──────────── │  │ ──────────── │  │ ──────────── │  │ ──────────── │
│  secret key  │  │  secret key  │  │  secret key  │  │  secret key  │
│    'sk-1'    │  │    'sk-2'    │  │    'sk-3'    │  │    'sk-4'    │
└──────────────┘  └──────────────┘  └──────────────┘  └──────────────┘

为了实现上述功能,引入了提供者概念。你可以定义一个密钥、回调或重定向URL。下面是一个示例。请注意,SKA_PROVIDERS(“client_1”,“client_2”等)的键是提供者密钥。

SKA_PROVIDERS = {
    # ********************************************************
    # ******************** Basic gradation *******************
    # ********************************************************
    # Site 1
    'client_1': {
        'SECRET_KEY': 'sk-1',
    },

    # Site 2
    'client_2': {
        'SECRET_KEY': 'sk-2',
    },

    # Site 3
    'client_3': {
        'SECRET_KEY': 'sk-3',
    },

    # Site 4
    'client_4': {
        'SECRET_KEY': 'sk-4',
    },

    # ********************************************************
    # ******* You make gradation as complex as you wish ******
    # ********************************************************
    # Client 1, group users
    'client_1.users': {
        'SECRET_KEY': 'client-1-users-secret-key',
    },

    # Client 1, group power_users
    'client_1.power_users': {
        'SECRET_KEY': 'client-1-power-users-secret-key',
        'USER_CREATE_CALLBACK': 'foo.ska_callbacks.client1_power_users_create',
    },

    # Client 1, group admins
    'client_1.admins': {
        'SECRET_KEY': 'client-1-admins-secret-key',
        'USER_CREATE_CALLBACK': 'foo.ska_callbacks.client1_admins_create',
        'REDIRECT_AFTER_LOGIN': '/admin/'
    },
}

请参阅回调部分以获取回调列表。请注意,在SKA_PROVIDERS中定义的回调是覆盖。如果某个回调未在SKA_PROVIDERS中定义,则身份验证后端将回退到相应的默认回调函数。

显然,服务器必须定义完整的提供者列表。在客户端,你只需要存储通用密钥和当然提供者UID。

当在发送端创建签名URL时,你应该在extra参数中提供provider密钥。下面是一个如何为client_1.power_users执行的示例。

from ska import sign_url
from ska.defaults import DEFAULT_PROVIDER_PARAM

server_ska_login_url = 'https://server-url.com/ska/login/'

signed_remote_ska_login_url = sign_url(
    auth_user='test_ska_user',
    # Using provider-specific secret key. This value shall be equal to
    # the value of SKA_PROVIDERS['client_1.power_users']['SECRET_KEY'],
    # defined in your projects' Django settings module.
    secret_key='client-1-power-users-secret-key',
    url=server_ska_login_url,
    extra={
        'email': 'test_ska_user@mail.example.com',
        'first_name': 'John',
        'last_name': 'Doe',
        # Using provider specific string. This value shall be equal to
        # the key string "client_1.power_users" of SKA_PROVIDERS,
        # defined in your projcts' Django settings module.
        DEFAULT_PROVIDER_PARAM: 'client_1.power_users',
    }
)

Django模型方法装饰器sign_url

这最有可能用于模块models(models.py)。

想象一下,你有一些对象列表,并且只想让授权方查看URL。你可以在渲染列表(HTML)时使用get_signed_absolute_url方法来保护这些URL。

from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse

from ska.contrib.django.ska.decorators import sign_url


class FooItem(models.Model):

    title = models.CharField(_("Title"), max_length=100)
    slug = models.SlugField(unique=True, verbose_name=_("Slug"))
    body = models.TextField(_("Body"))

    # Unsigned absolute URL, which goes to the foo item detail page.
    def get_absolute_url(self):
        return reverse('foo.detail', kwargs={'slug': self.slug})

    # Signed absolute URL, which goes to the foo item detail page.
    @sign_url()
    def get_signed_absolute_url(self):
        return reverse('foo.detail', kwargs={'slug': self.slug})

请注意,sign_url装饰器接受以下可选参数。

  • auth_userstr):发起请求的用户的用户名。

  • secret_key:共享密钥。如果设置,则覆盖在项目中设置模块的SKA_SECRET_KEY变量。

  • valid_untilfloatstr):Unix时间戳。如果没有给出,则自动生成(现在 + 寿命)。

  • lifetimeint):签名寿命(秒)。

  • suffixstr):添加到endpoint_url之后并在附加签名参数之前的后缀。

  • signature_paramstr):将包含生成的签名值的GET参数名称。

  • auth_user_paramstr):将包含auth_user值的GET参数名称。

  • valid_until_paramstr):将包含valid_until值的GET参数名称。

Django视图装饰器validate_signed_request

用于保护视图(file views.py)。应应用于需要签名请求的视图(端点)。如果不成功,将返回ska.contrib.django.ska.http.HttpResponseUnauthorized,它是Django的django.http.HttpResponse的子类。你可以提供自己的401错误模板。只需将设置模块中的SKA_UNAUTHORISED_REQUEST_ERROR_TEMPLATE指向正确的模板即可。请参阅ska/contrib/django/ska/templates/ska/401.html作为模板示例。

from ska.contrib.django.ska.decorators import validate_signed_request

# Your view that shall be protected
@validate_signed_request()
def detail(request, slug, template_name='foo/detail.html'):
    # Your code

请注意,validate_signed_request装饰器接受以下可选参数。

  • secret_key (str) : 共享密钥。如果设置,将覆盖项目中设置模块中设置的 SKA_SECRET_KEY 变量。

  • signature_param (str): 存储签名字符串的(例如 GET 或 POST)参数名称。

  • auth_user_param (str): 存储认证用户字符串的(例如 GET 或 POST)参数名称。

  • valid_until_param (str): 存储有效直到日期字符串的(例如 GET 或 POST)参数名称。

如果你使用基于类的视图,请使用 m_validate_signed_request 装饰器代替 validate_signed_request

模板标签

有两个模板标签模块: ska_tagsska_constance_tags。它们功能相同,尽管 ska_constance_tagsdjango-constance 相关。

对于标准设置配置,模板标签应按以下方式加载

{% load ska_tags %}

对于基于 django-constance 的设置配置,模板标签应按以下方式加载

{% load ska_constance_tags %}

注意,如果你想使用 ska_constance_tags,请将 ska.contrib.django.ska.integration.constance_integration 行添加到你的 `INSTALLED_APPS`。

INSTALLED_APPS = (
    # ...
    'ska.contrib.django.ska.integration.constance_integration',
    # ...
)
sign_url

sign_url 模板标签接受模板上下文和以下参数

  • url

  • auth_user: 如果未提供,则使用 request.user.get_username()

  • secret_key: 如果未提供,则使用设置中的密钥。

  • valid_until: 如果未提供,则从 lifetime 计算得出。

  • lifetime: 默认为 ska.defaults.SIGNATURE_LIFETIME

  • suffix: 默认为 ska.defaults.DEFAULT_URL_SUFFIX

  • signature_param: 默认为 ska.defaultsDEFAULT_SIGNATURE_PARAM

  • auth_user_param: 默认为 ska.defaults.DEFAULT_AUTH_USER_PARAM

  • valid_until_param: 默认为 ska.defaults.DEFAULT_VALID_UNTIL_PARAM

  • signature_cls: 默认为 ska.signatures.Signature

使用示例

{% load ska_tags %}

{% for item in items%}

    {% sign_url item.get_absolute_url as item_signed_absolute_url %}
    <a href="{{ item_signed_absolute_url }}">{{ item }}</a>

{% endfor %}
provider_sign_url

provider_sign_url 模板标签接受模板上下文和以下参数

  • url

  • provider: 提供者名称。

  • auth_user: 如果未提供,则使用 request.user.get_username()

  • valid_until: 如果未提供,则从 lifetime 计算得出。

  • lifetime: 默认为 ska.defaults.SIGNATURE_LIFETIME

  • suffix: 默认为 ska.defaults.DEFAULT_URL_SUFFIX

  • signature_param: 默认为 ska.defaultsDEFAULT_SIGNATURE_PARAM

  • auth_user_param: 默认为 ska.defaults.DEFAULT_AUTH_USER_PARAM

  • valid_until_param: 默认为 ska.defaults.DEFAULT_VALID_UNTIL_PARAM

  • signature_cls: 默认为 ska.signatures.Signature

  • fail_silently: 默认为 False。

使用示例

{% load ska_tags %}

{% for item in items%}

    {% provider_sign_url url=item.get_absolute_url provider='client_1.users' as item_signed_absolute_url %}
    <a href="{{ item_signed_absolute_url }}">{{ item }}</a>

{% endfor %}

身份验证后端

允许你获得 Django 网站的免密码登录。

目前有两个后端实现

默认情况下,使用相同令牌的登录次数不受限制。如果你希望在第一次使用后令牌失效,请将以下变量设置为 True 在你的项目的 Django 设置模块中。

SKA_DB_STORE_SIGNATURES = True
SKA_DB_PERFORM_SIGNATURE_CHECK = True
SkaAuthenticationBackend

SkaAuthenticationBackend 使用标准 Django 设置。

接收方

收件人是尝试进行身份验证(登录)的主机(Django 网站)。在收件人端,以下内容应存在。

settings.py
AUTHENTICATION_BACKENDS = (
    'ska.contrib.django.ska.backends.SkaAuthenticationBackend',
    'django.contrib.auth.backends.ModelBackend',
)

INSTALLED_APPS = (
    # ...
    'ska.contrib.django.ska',
    # ...
)

SKA_SECRET_KEY = 'secret-key'
SKA_UNAUTHORISED_REQUEST_ERROR_TEMPLATE = 'ska/401.html'
SKA_REDIRECT_AFTER_LOGIN = '/foo/logged-in/'
urls.py
urlpatterns = [
    url(r'^ska/', include('ska.contrib.django.ska.urls')),
    url(r'^admin/', include(admin.site.urls)),
]
回调函数

已经为身份验证后端实现了几个回调函数。

  • USER_VALIDATE_CALLBACK (str): 验证请求回调。创建用于允许添加自定义逻辑到传入的认证请求。主要目的是提供一个灵活的方式来抛出异常,如果传入的认证请求应该被阻止(例如,电子邮件或用户名在黑名单中,或者正好相反——不在白名单中)。USER_VALIDATE_CALLBACK的唯一目的是如果请求数据无效,则抛出django.core.PermissionDenied异常。在这种情况下,认证流程将中断。其他所有异常都将被简单地忽略(但记录),如果没有抛出异常,则继续正常流程。

  • USER_GET_CALLBACK (str): 如果成功从数据库中获取用户(现有用户),则触发。

  • USER_CREATE_CALLBACK (str): 用户创建后立即触发(用户不存在)。

  • USER_INFO_CALLBACK (str): 成功认证时触发。

回调函数的示例(假设它位于模块my_app.ska_callbacks中)

def my_callback(user, request, signed_request_data)
    # Your code

…其中

  • userdjango.contrib.auth.models.User实例。

  • requestdjango.http.HttpRequest实例。

  • signed_request_data是包含已签名请求数据的字典。

例如,如果您需要将用户分配到某些本地Django组,您可以在客户端指定组名(将其添加到extra字典中),然后在回调中根据该名称将用户添加到该组。

回调是回调函数的路径限定符。考虑到上面的例子,它将是my_app.ska_callbacks.my_callback

在项目的设置模块中,为每个回调变量的名称前加上SKA_

示例

SKA_USER_GET_CALLBACK = 'my_app.ska_callbacks.my_get_callback'
SKA_USER_CREATE_CALLBACK = 'my_app.ska_callbacks.my_create_callback'
发送方

发送者是用户通过使用已签名的URL认证到接收者的主机(另一个Django网站)。

在发送端,只需要存在Django的ska模块,以及当然是在服务器端相同的SECRET_KEY。进一步来说,服务器的ska登录URL(在我们的案例中是“/ska/login/”)应该使用ska进行签名(例如,使用sign_url函数)。auth_user参数将用作Django用户名。请参阅下面的示例。

from ska import sign_url
from ska.contrib.django.ska.settings import SECRET_KEY

server_ska_login_url = 'https://server-url.com/ska/login/'

signed_url = sign_url(
    auth_user='test_ska_user_0',
    secret_key=SECRET_KEY,
    url=server_ska_login_url,
    extra={
        'email': 'john.doe@mail.example.com',
        'first_name': 'John',
        'last_name': 'Doe',
    }
)

注意,extra字典是可选的!如果存在emailfirst_namelast_name键,在验证成功后,数据将被保存到用户的个人资料中。

将此代码放入视图,然后在模板上下文中提供生成的URL,并将其渲染为URL,以便用户可以点击它进行服务器认证。

def auth_to_server(request, template_name='auth_to_server.html'):
    # Some code + obtaining the `signed_url` (code shown above)
    context = {'signed_url': signed_url}

    return render(request, template_name, context)
SkaAuthenticationConstanceBackend

依赖于由django-constance提供的动态设置功能。

仅提及与`SkaAuthenticationBackend`的差异。

settings.py
AUTHENTICATION_BACKENDS = (
    'ska.contrib.django.ska.backends.constance_backend.SkaAuthenticationConstanceBackend',
    'django.contrib.auth.backends.ModelBackend',
)

INSTALLED_APPS = (
    # ...
    'constance',  # django-constance
    'ska.contrib.django.ska',
    'django_json_widget',  # For nice admin JSON widget
    # ...
)

CONSTANCE_CONFIG = {
    'SKA_PROVIDERS': (
        {},  # The default value
        'JSON data',  # Help text in admin
        'JSONField_config',  # Field config
    )
}

CONSTANCE_ADDITIONAL_FIELDS = {
    'JSONField_config': [
        # `jsonfield2` package might be used for storing the JSON field,
        # however, at the moment of writing it has a bug which makes
        # the JSON invalid after the first save. To avoid that, it has
        # been patched and resides in examples/simple/jsonfield2_addons/
        # module.
        'jsonfield2_addons.forms.JSONField',
        {
            'widget': 'django_json_widget.widgets.JSONEditorWidget',
        }
    ],
}

CONSTANCE_BACKEND = 'constance.backends.redisd.RedisBackend'

CONSTANCE_REDIS_CONNECTION = {
    'host': 'localhost',
    'port': 6379,
    'db': 0,
}

使用 DatabaseBackend 的样子如下

CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'

INSTALLED_APPS = (
    # ...
    'constance.backends.database',
    # ...
)

动态后端的快速演示

  • 克隆此项目

git clone git@github.com:barseghyanartur/ska.git
  • 安装/迁移

./scripts/install.sh
pip install -r examples/requirements/django_2_1.txt
./scripts/migrate.sh --settings=settings.constance_settings
  • 运行

./scripts/runserver.sh --settings=settings.constance_settings
{
   "client_1.users":{
      "SECRET_KEY":"client-1-users-secret-key"
   },
   "client_1.power_users":{
      "SECRET_KEY":"client-1-power-users-secret-key",
      "USER_CREATE_CALLBACK":"foo.ska_callbacks.client1_power_users_create"
   },
   "client_1.admins":{
      "SECRET_KEY":"client-1-admins-secret-key",
      "USER_CREATE_CALLBACK":"foo.ska_callbacks.client1_admins_create",
      "USER_GET_CALLBACK":"foo.ska_callbacks.client1_admins_get",
      "USER_INFO_CALLBACK":"foo.ska_callbacks.client1_admins_info_constance",
      "REDIRECT_AFTER_LOGIN":"/admin/auth/user/"
   }
}
  • 在另一个浏览器中打开 http://localhost:8000/foo/authenticate/ 并导航到 By provider 部分的 Success 表格列中的 Log in - client_1.admins 链接。点击后,您应该已登录。您已使用动态设置。

urls.py

django-constance 特定的视图和 URL 已使用。有关参考,请参阅 ska.contrib.django.ska.views.constance_viewsska.contrib.django.ska.urls.constance_urls

urlpatterns = [
    url(r'^ska/', include('ska.contrib.django.ska.urls.constance_urls')),
    url(r'^admin/', include(admin.site.urls)),
]
自定义身份验证后端

要实现替代身份验证后端,请参阅以下示例

from constance import config

from ska.contrib.django.backends import BaseSkaAuthenticationBackend

class SkaAuthenticationConstanceBackend(BaseSkaAuthenticationBackend):
    """Authentication backend."""

    def get_settings(self):
        """

        :return:
        """
        return config.SKA_PROVIDERS

就是这样。get_settings 方法应该返回的只是一个包含提供者数据的 dict(有关参考,请参阅多个密钥;`get_settings` 的返回值是 `SKA_PROVIDERS` 字典)。

清除旧签名数据

如果您有大量访客且将 SKA_DB_STORE_SIGNATURES 设置为 True,则您的数据库会增长。如果您想删除旧的签名令牌数据,您可能需要通过 cron job 执行以下命令。

./manage.py ska_purge_stored_signature_data
安全注意事项

从安全角度出发,您应该通过安全的 HTTP 连接提供服务以下页面

  • 服务器登录页面 (/ska/login/).

  • 包含身份验证链接的客户端页面。

Django REST 框架集成

权限类

为了保护未经系统实际认证的视图,已实现了特定的权限类(对于计划设置和提供者设置,以及平面和提供者设置与 django-constance 包结合使用)。

实现了以下权限类

  • SignedRequestRequired

  • ProviderSignedRequestRequired

  • ConstanceSignedRequestRequired

  • ConstanceProviderSignedRequestRequired

ProviderSignedRequestRequired 示例

from rest_framework.viewsets import ModelViewSet

from ska.contrib.django.ska.integration.drf.permissions import (
    ProviderSignedRequestRequired
)

from .models import FooItem
from .serializers import FooItemSerializer

class FooItemViewSet(ModelViewSet):
    """FooItem model viewset."""

    permission_classes = (ProviderSignedRequestRequired,)
    queryset = FooItem.objects.all()
    serializer_class = FooItemSerializer

签名请求

请求以相同的方式进行签名。示例代码

# Given that we have `auth_user`, `auth_user_email`, `provider_name`
# (and the rest), the code would look as follows:

from ska import sign_url
from ska.defaults import DEFAULT_PROVIDER_PARAM

extra = {
    'email': auth_user_email,
    'first_name': first_name,
    'last_name': last_name,
}

if provider_name:
    extra.update({DEFAULT_PROVIDER_PARAM: provider_name})

signed_url = sign_url(
    auth_user=auth_user,
    secret_key=secret_key,
    url=url,
    extra=extra
)
JWT 令牌用于身份验证

用于获取 JWT 令牌进行身份验证。这也与 django-constance 一起工作。

设置示例

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

URL 示例

urlpatterns = [
    # ...
    url(
        r'^ska-rest/',
        include('ska.contrib.django.ska.integration.drf.urls.jwt_token')
    ),
]

示例请求

http://localhost:8008/ska-rest/obtain-jwt-token/
    ?signature=P92KWDDe0U84Alvu0tvmYoi8e8s%3D
    &auth_user=test_ska_user
    &valid_until=1548195246.0
    &extra=email%2Cfirst_name%2Clast_name
    &email=test_ska_user%40mail.example.com
    &first_name=John
    &last_name=Doe

示例响应

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
    "token": "eyJ0eXAiO.eyJ1c2VyX2lkIjo.m_saOvyKBO3"
}

测试

只需键入

pytest

或使用 tox

tox

或使用 tox 检查特定环境

tox -e py39

或运行 Django 测试

python examples/simple/manage.py test ska --settings=settings.testing

编写文档

请保持以下层次结构。

=====
title
=====

header
======

sub-header
----------

sub-sub-header
~~~~~~~~~~~~~~

sub-sub-sub-header
++++++++++++++++++

sub-sub-sub-sub-header
^^^^^^^^^^^^^^^^^^^^^^

sub-sub-sub-sub-sub-header
**************************

许可协议

GPL-2.0-only OR LGPL-2.1-or-later

支持

有关任何问题,请联系作者部分提供的电子邮件。

作者

Artur Barseghyan <artur.barseghyan@gmail.com>

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分发

ska-1.10.tar.gz (92.9 kB 查看哈希值)

上传时间

构建分发

ska-1.10-py2.py3-none-any.whl (96.0 kB 查看哈希值)

上传时间 Python 2 Python 3

支持