阿里云 身份提供商_编写角色身份提供者

news/2024/7/19 16:20:55 标签: python, javascript, java, js, 编程语言

阿里云 身份提供商

As I said in my last post, I recently added Persona Identity Provider functionality to this blog. If you follow me on Twitter (and really, why wouldn’t you?), you might have noticed that this wasn’t an entirely smooth process.

正如我在上一篇文章中所述,我最近在此博客中添加了Persona Identity Provider功能。 如果您在Twitter上关注我(真的,为什么不呢?),您可能已经注意到这并不是一个完全顺利的过程。

Mozilla are a great organisation, and there are lots of great developers working on the Persona project. However, the official documentation is of varying quality. Large sections of it are very clear. Unfortunately, there are significant chunks that range from incomplete to outright contradictory. Some parts appear to be out of date. Other parts specify that they follow a third-party specification, only to deviate from that specification without apparent reasoning.

Mozilla是一个很棒的组织,在Persona项目上有很多很棒的开发人员。 但是,官方文档的质量各不相同。 它的大部分内容都很清楚。 不幸的是,有很多重要的块,从不完整到完全矛盾。 某些零件似乎已过时。 其他部分指定它们遵循第三方规范,而只是在没有明显理由的情况下偏离该规范。

This makes implementing a complying Identity Provider something of a moving target. If you’re running a Node.js-based website then Mozilla kindly provide a reference implementation, but otherwise the only really effective way of working out what you’re supposed to do in these ambiguous situations is to undertake a code-reading of the Node.js reference implementation to see the expected behaviour.

这使得实现合规的身份提供者成为移动目标。 如果您正在运行基于Node.js的网站,那么Mozilla会提供参考实现,但是,否则,在这些模棱两可的情况下确定您应该执行的操作的唯一真正有效的方法是对代码进行读取。 Node.js参考实现以查看预期的行为。

So I wanted to be as clear as possible about what you need to do to implement a Persona IdP. I’ll be focusing on Python, but as much as possible I’ll try to describe things in a generic, cross-platform kind of way.

因此,我想尽可能清楚地了解实现Persona IdP需要做什么。 我将专注于Python,但是我将尽可能尝试以一种通用的,跨平台的方式描述事物。

你需要什么 (What You Need)

You will need to implement the following services.

您将需要实施以下服务。

  1. You will need to serve a browser support document. This document will be fetched by each website that asks you to verify someone’s identity. It’s a JSON-formatted document that contains a public key and the URL of two of the other services.
  2. You will need to provide a login page. This login page will need to call some Persona-specific Javascript, so depending on how flexible your website is you may or may not be able to re-use one of your already existing login pages.
  3. You will need to provide a ‘provisioning’ page. This will be loaded in a background frame during the login process, so don’t worry about styling it. The provisioning page is basically just a glue page: it works out if the user is logged in, and tells the Persona API about it.
  4. You will need a way to receive data from the browser and sign it. Bizarrely, the current Persona docs omit anything about this portion of the process.
  1. 您将需要提供浏览器支持文档。 每个要求您验证某人身份的网站都将获取此文档。 这是一个JSON格式的文档,其中包含公钥和其他两个服务的URL。
  2. 您将需要提供一个登录页面。 此登录页面将需要调用某些Persona特定的Javascript,因此,取决于您网站的灵活性,您是否可以重用您已经存在的登录页面之一。
  3. 您将需要提供一个“配置”页面。 这将在登录过程中加载到背景框架中,因此不必担心其样式。 设置页面基本上只是一个粘合页面:它可以确定用户是否登录,并告知Persona API。
  4. 您将需要一种从浏览器接收数据并对其进行签名的方法。 奇怪的是,当前的Persona文档忽略了该过程的这一部分。

For the rest of this article, I’m going to assume you already have a working website. With that said, I’m also going to assume your website is pretty damn basic: much like mine.

对于本文的其余部分,我将假定您已经有一个正常工作的网站。 话虽如此,我还要假设您的网站非常简单:非常像我的。

You’ll need to serve the Persona pages over HTTPS. Given that you will have to do this anyway, you should also take this opportunity to just serve your whole site over HTTPS. This means you’ll need to get an SSL certificate. I’m not going to go into detail right now, but you can find plenty of guides about how to do so.

您将需要通过HTTPS提供角色页面。 鉴于您仍然必须执行此操作,因此您还应该借此机会通过HTTPS为整个站点提供服务 。 这意味着您需要获取SSL证书。 我现在不准备详细介绍,但是您可以找到很多有关此操作的指南。

支持文件 (The Support Document)

Let’s just get started. The very first thing you’re going to want to do is to generate an RSA key. Persona relies on public key cryptography for authentication, and that means you need a key pair. If you’re on Mac OS X or Linux, you should already have OpenSSL installed. No guarantees for those of you on Windows. Make sure you have it, then run the following two commands in a command shell:

让我们开始吧。 您要做的第一件事就是生成RSA密钥。 Persona依靠公钥加密进行身份验证,这意味着您需要一个密钥对。 如果您使用的是Mac OS X或Linux,则应该已经安装了OpenSSL 。 在Windows上不为您提供任何保证。 确保已安装,然后在命令外壳中运行以下两个命令:

openssl genrsa -out private-key.pem 2048
openssl rsa -in private-key.pem -pubout > public-key.pem
openssl genrsa -out private-key.pem 2048
openssl rsa -in private-key.pem -pubout > public-key.pem
 

You now have two files. If you want, you can check your public key into your revision control software of choice. I strongly recommend you do not check the private key in, for security reasons.

您现在有两个文件。 如果需要,可以将公共密钥签入所选的版本控制软件中。 出于安全原因, 我强烈建议您不要检查中的私钥

Next, decide what URLs you want to serve your provisioning and login pages on. For my website, I chose /persona/provision/ and /persona/sign_in/ respectively.

接下来,确定要在其上提供配置和登录页面的URL。 对于我的网站,我分别选择了/persona/provision//persona/sign_in/

When you have all that sorted, you’ve got everything you need to create your support document. This must be served from a specific URL (it’s the only thing you don’t get to choose): specifically, /.well-known/browserid/.

完成所有排序后,您便拥有了创建支持文档所需的一切。 这必须通过特定的URL(这是您唯一要选择的URL)提供:具体地说,是/.well-known/browserid/

The support document is a JSON-formatted page, which contains three pieces of information: your public key (in a confusing format I’ll describe shortly), the URL for your login page and the URL for your provisioning page. To see my version of this file, click here.

支持文档是一个JSON格式的页面,其中包含三部分信息:您的公钥(我将在稍后描述的一种令人困惑的格式),您的登录页面的URL和您的供应页面的URL。 要查看此文件的我的版本, 请单击此处 。

The JSON should look like this (the XXXX values are placeholders I’m using to hide long strings of data):

JSON应该看起来像这样(XXXX值是我用来隐藏长数据字符串的占位符):

You should be able to see where to put the two URLs you just created, but filling in the public key object is harder. To get the strings you need to put in place of each XXXX, I recommend you run the following Python script on your machine. It takes one argument: the path to your private key file.

您应该能够看到刚刚创建的两个URL的放置位置,但是填充公钥对象会比较困难。 要获取需要替换每个XXXX的字符串,建议您在计算机上运行以下Python脚本。 它有一个参数:私钥文件的路径。

#!/usr/bin/env python
#!/usr/bin/env python
import import subprocess
subprocess
import import sys

sys

def def keyfield_as_intkeyfield_as_int (( field_namefield_name , , datadata ):
    ):
    start start = = datadata .. indexindex (( field_name field_name + + ':'':' ) ) + + 1
    1
    index index = = start

    start

    while while (( TrueTrue ):
        ):
        index index += += 1

        1

        if if not not datadata [[ indexindex ]] .. startswithstartswith (( ' '' ' ):
            ):
            break

    break

    result result = = outputoutput [[ startstart :: indexindex ]
    ]
    result result = = '''' .. joinjoin ([([ xx .. stripstrip () () for for x x in in resultresult ])]) .. replacereplace (( ':'':' , , '''' )

    )

    return return intint (( resultresult , , 1616 )

)

def def get_public_exponentget_public_exponent (( datadata ):
    ):
    for for element element in in datadata :
        :
        if if elementelement .. startswithstartswith (( 'publicExponent''publicExponent' ):
            ):
            return return elementelement .. splitsplit (( ' '' ' )[)[ 11 ]

]

keyfile keyfile = = syssys .. argvargv [[ 11 ]
]
proc proc = = subprocesssubprocess .. PopenPopen ([([ 'openssl''openssl' , , 'rsa''rsa' , , '-in''-in' , , keyfilekeyfile , , '-text''-text' , , '-noout''-noout' ],
                        ],
                        stdoutstdout == subprocesssubprocess .. PIPEPIPE )
)
output output = = procproc .. communicatecommunicate ()[()[ 00 ]] .. splitsplit (( '' nn '' )

)

modulus modulus = = keyfield_as_intkeyfield_as_int (( 'modulus''modulus' , , outputoutput )
)
privateExponent privateExponent = = keyfield_as_intkeyfield_as_int (( 'privateExponent''privateExponent' , , outputoutput )
)
publicExponent publicExponent = = get_public_exponentget_public_exponent (( outputoutput )

)

print print '' nn '' .. joinjoin ([([ 'Modulus (n):''Modulus (n):' , , strstr (( modulusmodulus ),
                 ),
                 'Public Exponent (e):''Public Exponent (e):' , , strstr (( publicExponentpublicExponent ),
                 ),
                 'Private Exponent (d):''Private Exponent (d):' , , strstr (( privateExponentprivateExponent )]))])

The script prints out three things, which are given the letters n, e and d. How exactly these relate to your private and public keys is left as an exercise for the interested reader. What you care about right now is that this script provides exactly the values for n and e in your support document. Wrap them in quotes so they become a JSON string, and then set them in the right place (one of the XXXX locations above). Keep hold of the value for d, we’ll need it later.

该脚本打印出三样东西,分别以字母n,e和d表示。 这些内容与您的私钥和公钥的确切关系留给有兴趣的读者练习。 您现在关心的是,该脚本准确地提供了支持文档中的n和e值。 将它们用引号引起来,使它们成为JSON字符串,然后将它们设置在正确的位置(上述XXXX位置之一)。 保留d的值,稍后我们将需要它。

With that step finished, your support document should be done! Make sure you serve it from the correct URL, with the correct Content-Type header set, and then you’re good to go.

完成该步骤后,应该完成您的支持文档! 确保从设置了正确的Content-Type标头的正确URL提供服务,然后您就可以开始使用了。

登录页面 (The Login Page)

Let’s tackle the login page next. This page should only respond to a HTTP GET request. It is shown directly to the user, so you want to apply your CSS styles to it so that the user can recognise it as yours. If you already have a login page, I recommend you either extend or copy it.

接下来处理登录页面。 该页面应仅响应HTTP GET请求。 它直接显示给用户,因此您想对其应用CSS样式,以便用户可以将其识别为您的样式。 如果您已经有一个登录页面,建议您扩展或复制它。

This page needs to be able to do two things: tell if your user is already logged in, and be able to log them in if they aren’t. The second one of these is left as an exercise to the reader, but your web framework of choice is likely to provide you with useful tools. If you’re using Django, I highly recommend simply using the default authentication methods, it’ll work great.

该页面必须能够做两件事:告诉您的用户是否已经登录,如果尚未登录,则能够登录。 这些中的第二个留给读者练习,但是您选择的Web框架可能会为您提供有用的工具。 如果您使用的是Django,我强烈建议您仅使用默认的身份验证方法,它将很好地工作。

The reason you can’t just use your normal login page is that you need some extra stuff. Firstly, you need to include some more Javascript. Add this line somewhere to your code (I recommend the header, against most performance advice. You’ll see why in a second):

您不能只使用常规登录页面的原因是您需要一些额外的东西。 首先,您需要包含更多Javascript。 将此行添加到代码中的某个位置(我建议使用标头,而不是大多数性能建议。稍后,您将看到原因):

After the place you add that line, you’ll want to add some inline Javascript. This should immediately call navigator.id.beginAuthentication(). This function, provided by Persona, takes a single argument: a callback function that itself takes only one argument, the email address being verified. Inside the callback, you want to check whether the user is logged in. If they are, you’ll want to call navigator.id.completeAuthentication().

在添加该行的位置之后,您需要添加一些内联Javascript。 这应该立即调用navigator.id.beginAuthentication() 。 由Persona提供的此函数有一个参数:一个回调函数,其本身仅包含一个参数,该电子邮件地址已验证。 在回调内部,您要检查用户是否已登录。如果已登录,则需要调用navigator.id.completeAuthentication()

(Personal side note: I actually did this confirmation server-side. If the user had an active session cookie, I unconditionally called completeAuthentication(). Do whatever is easiest for you.)

(个人注意事项:我实际上是在服务器端进行此确认的。如果用户有活动的会话cookie,我将无条件地调用completeAuthentication() 。执行对您来说最简单的操作。)

An example code block would be:

示例代码块为:

navigatornavigator .. idid .. beginAuthenticationbeginAuthentication (( functionfunction (( emailemail ) ) {
    {
    if if userHasActiveSessionuserHasActiveSession (( emailemail ) ) {
        {
        navigatornavigator .. idid .. completeAuthenticationcompleteAuthentication ();
    ();
    }
}
});});

Here, userHasActiveSession would check whether the user in question has an active login session with your domain.

在这里, userHasActiveSession将检查相关用户是否与您的域具有活动的登录会话。

If it turns out they do, the call to navigator.id.completeAuthentication() will immediately route them away from your login page, so nothing will happen. Otherwise, the user will be faced with your login page. They’ll be asked to log in, and then will be looped back to the beginning of the Persona authentication process.

如果事实证明确实如此,则对navigator.id.completeAuthentication()的调用将立即将其路由到您的登录页面之外,因此不会发生任何事情。 否则,用户将面临您的登录页面。 将要求他们登录,然后将其循环回到Persona身份验证过程的开始。

If you need to signal any failure, either an error or the user cancelling, just call the navigator.id.raiseAuthenticationFailure() function. This takes a single argument: a string indicating the reason for failure. Calling this function will abort the Persona login process.

如果您需要发信号通知任何故障(错误或用户取消),只需调用navigator.id.raiseAuthenticationFailure()函数。 它仅包含一个参数:指示失败原因的字符串。 调用此函数将中止Persona登录过程。

In some ways, this page is even easier to write than the support document. This shouldn’t take you too long, especially if you already have a login page.

在某些方面,此页面比支持文档更容易编写。 这不需要花太长时间,尤其是如果您已经有一个登录页面。

设置页面 (The Provisioning Page)

This is the hardest of the actual web pages to build, and take heart my friend, because it’s not that hard either. This page is not user-facing. Persona will load this in an invisible frame, so all that matters is the Javascript. Note, however, that you must allow this page to be served in a frame! This means no setting the X-Frame-Options header to DENY!

这是构建实际网页中最困难的一个,并且让我的朋友振作起来,因为它也不难。 此页面不是面向用户的。 Persona会将其加载到一个不可见的框架中,因此重要的是Javascript。 但是请注意,您必须允许将此页面放在框架中提供! 这意味着无需将X-Frame-Options标头设置为DENY

This page needs to load some Javascript, and then execute some Javascript. Begin by including the following:

该页面需要加载一些Javascript,然后执行一些Javascript。 首先包括以下内容:

After adding that line, you’ll want to add some inline Javascript. This should call a function that then calls navigator.id.beginProvisioning(). This function takes a callback: the callback takes two arguments, the user’s email address (a String) and the requested certificate duration (a Number).

添加该行之后,您需要添加一些内联Javascript。 这应该调用一个函数,然后调用navigator.id.beginProvisioning() 。 此函数采用回调:回调采用两个参数,即用户的电子邮件地址(字符串)和请求的证书期限(数字)。

Inside the callback, you need to work out whether the user has an active session with your domain, and whether it’s for the email address requested. You can repeat the logic used in the Login Page for this.

在回调中,您需要确定用户是否与您的域进行了活动会话,以及是否针对所请求的电子邮件地址。 您可以为此重复登录页面中使用的逻辑。

WARNING: If the user has third-party cookies disabled, they won’t send their session cookie to this page. I consider this a failure case, and for the sake of security I strongly recommend you do too.

警告:如果用户禁用了第三方cookie,则不会将其会话cookie发送到此页面。 我认为这是一个失败案例,出于安全考虑,我强烈建议您也这样做。

If the user doesn’t have an active session, you will want to immediately call navigator.id.raiseProvisioningFailure(). This takes an error message as its first parameter, which you should provide. Calling this will abort the process and pass the user to your login page.

如果用户没有活动的会话,则需要立即调用navigator.id.raiseProvisioningFailure() 。 这会将错误消息作为第一个参数,您应该提供它。 调用此选项将中止该过程,并将用户传递到您的登录页面。

If the user does have an active session, you’ll want to next call navigator.id.getKeyPair(). This takes a callback: the callback takes one argument, which is the user’s public key (a String).

如果用户确实有活动的会话,则接下来要调用navigator.id.getKeyPair() 。 这需要一个回调:回调带有一个参数,即用户的公共密钥(一个String)。

Inside the callback for this function, you will need to send the user’s public key and the requested certificate duration (originally from navigator.id.beginProvisioning()) to your server. Your server will then need to generate what Mozilla calls a ‘signed assertion’. We’ll come back to how to do this. In the meantime, let’s just encapsulate that logic in a function called signServerSide().

在此函数的回调内部,您需要将用户的公钥和请求的证书持续时间(最初从navigator.id.beginProvisioning() )发送到服务器。 然后,您的服务器将需要生成Mozilla所谓的“签名断言”。 我们将回到如何做到这一点。 同时,让我们将该逻辑封装在一个名为signServerSide()的函数中。

Once you’ve got the signed certificate (passed by signServerSide() into a callback), you will want to pass it as the only argument to navigator.id.registerCertificate(). This instructs Persona to store the assertion in the browser, allowing users to login as themselves in Persona websites for up to the requested certificate duration.

获得签名证书(通过signServerSide()传递到回调中)后,您将需要将其作为navigator.id.registerCertificate()的唯一参数传递。 这指示Persona将断言存储在浏览器中,从而允许用户以自己的身份在Persona网站中登录,直到请求的证书期限。

Confused? Yeah, I would be too. An example code flow looks like this:

困惑? 是的,我也是。 示例代码流如下所示:

<script <script  type=type= "text/javascript>javascript""text/javascript>javascript" >
    >
    navigatornavigator .. idid .. beginProvisioningbeginProvisioning (( functionfunction (( emailemail , , certDurationcertDuration ) ) {
        {
        if if (( email email === === << EMAIL_FROM_SESSIONEMAIL_FROM_SESSION >> ) ) {
            {
            navigatornavigator .. idid .. genKeyPairgenKeyPair (( functionfunction (( publicKeypublicKey ) ) {
                {
                trytry {
                    {
                    signServerSidesignServerSide (( emailemail , , publicKeypublicKey , , certDurationcertDuration , , functionfunction (( certificatecertificate ) ) {
                        {
                        navigatornavigator .. idid .. registerCertificateregisterCertificate (( certificatecertificate );
                    );
                    });
                });
                } } catch catch (( ee ) ) {
                    {
                    navigatornavigator .. idid .. raiseProvisioningFailureraiseProvisioningFailure (( "unexpected error occurred""unexpected error occurred" );
                );
                }
            }
            });
        });
        } } else else {
            {
            navigatornavigator .. idid .. raiseProvisioningFailureraiseProvisioningFailure (( "user is not authenticated as target user""user is not authenticated as target user" );
        );
        }
    }
    });
});
</script></script>

I populate the <EMAIL_FROM_SESSION> space server-side, so it should be very difficult to interrupt the execution of this code block. The server-side signing code (we’ll come back to it) also checks, though, so this is not a single point of failure.

我在服务器端填充了<EMAIL_FROM_SESSION>空间,因此中断该代码块的执行应该非常困难。 不过,服务器端的签名代码(我们将继续讨论)也会进行检查,因此这不是单点故障。

This is all your sign-in page needs to do. We’ll tackle the signServerSide() function next.

这是您登录页面所需做的所有事情。 接下来,我们将解决signServerSide()函数。

证书签名第1部分:客户 (Certificate Signing Part 1: The Client)

We’re going to break the certificate signing into two parts. The first part is writing the signServerSide() function. This has a very simple role: it should upload the user’s certificate to the server, tell the server how long it should sign for, and then receive the response back.

我们将把证书签名分为两部分。 第一部分是编写signServerSide()函数。 它的作用非常简单:它将用户的证书上传到服务器,告诉服务器应该签名多长时间,然后将响应接收回来。

The way I recommend doing this is to use an HTTP POST. If you want, you can form-encode everything into the body, but I found it was easiest to provide the certificate duration (and the user’s email) as parameters of the query string, and to POST the (JSON-encoded) public key in the body.

我建议这样做的方法是使用HTTP POST。 如果需要,可以将所有内容都进行表单编码,但是我发现最简单的方法是提供证书持续时间(以及用户的电子邮件)作为查询字符串的参数,并在其中发布(JSON编码)公钥。身体。

You can do this however you want: if you’re already using JQuery, you may as well use it’s XMLHttpRequest functionality. I’m going to show you a code example that works with vanilla Javascript: no third-party libraries needed.

您可以根据需要执行此操作:如果已经在使用JQuery,则最好使用它的XMLHttpRequest功能。 我将向您展示适用于原始Javascript的代码示例:无需第三方库。

We begin by building up the URL. You’ll have to select what the relative URL for your signing service should be: I chose /persona/sign/. Your URL will be that, with a query string appended to it. To build the query string you’ll want to urlencode the email address and the query duration (always sanitise your inputs, guys), and build them into a query string.

我们首先构建URL。 您必须选择签名服务的相对URL应该是什么:我选择了/persona/sign/ 。 您的网址就是该网址,并附加了查询字符串。 要构建查询字符串,您需要对电子邮件地址和查询持续时间进行urlencode(始终清理输入内容,伙计们),然后将它们构建为查询字符串。

You’ll then want to create an XMLHttpRequest object and prepare it for POSTing to your newly-build URL. When that’s done, register an onreadystatechange event handler. This handler should check whether the request has completed, and if it has, call the callback with the returned data. If the request fails, it should raise an exception.

然后,您将要创建一个XMLHttpRequest对象,并准备将其发布到新建的URL。 完成后,注册一个onreadystatechange事件处理程序。 此处理程序应检查请求是否已完成,如果已完成,则使用返回的数据调用回调。 如果请求失败,则应引发异常。

Then, you’ll want to set the Content-Type header and launch the request.

然后,您将要设置Content-Type标头并启动请求。

My code for doing all this is below:

我执行此操作的代码如下:

This Javascript should be used on your Provisioning Page, as described in the relevant section. Next, we implement the server-side portion of the logic.

如相关部分所述,应在您的“设置页面”上使用此Javascript。 接下来,我们实现逻辑的服务器端部分。

证书签名第2部分:服务器 (Certificate Signing Part 2: The Server)

We need to implement some functionality on the server side of this, at the URL you created in the previous section. The server has a job that is simple to explain but not entirely simple to implement.

我们需要在服务器上,通过您在上一节中创建的URL来实现一些功能。 服务器的工作很容易解释,但实施起来并不完全简单。

The signing page should verify that the user is logged in, and that the email passed to it is the same as the email that is passed to it. The server should then generate the expiry time of the new certificate. Once that’s done, build the JSON-encoded assertion, then sign it with your private key. Finally, return all the data back in response.

签名页应验证用户已登录,并且传递给它的电子邮件与传递给它的电子邮件相同。 然后,服务器应生成新证书的到期时间。 完成后,构建JSON编码的断言,然后使用私钥对其进行签名。 最后,返回所有数据作为响应。

How you do much of this is dependent on your web framework and programming language. Your web framework should make it easy to verify that all the data you expect has been passed to you, and that it’s roughly the right shape. I won’t go into that portion of this stage in any detail. Instead, I’ll talk about some pitfalls you might encounter.

如何执行此操作取决于您的Web框架和编程语言。 您的Web框架应使您轻松验证所需的所有数据都已传递给您,并且它的形状大致正确。 在此阶段,我不会详细介绍。 相反,我将谈论您可能遇到的一些陷阱。

First, let’s consider the query duration. The format of the assertion you’re going to build requires that you provide the expiry time, in milliseconds since the epoch. All fairly standard. However, the query duration you’ve been passed is in seconds. You will need to multiply this by 1000 before adding it to the current time, which must also be in milliseconds past the epoch. This tripped me up twice, which says rather more about me than it does about this problem.

首先,让我们考虑查询持续时间。 要构建的断言的格式要求您提供有效期,以该时间段为单位的毫秒数。 一切都相当标准。 但是,您传递的查询持续时间以秒为单位。 您需要将其乘以1000,然后再将其添加到当前时间,该时间也必须是经过纪元后的毫秒数。 这使我绊倒了两次,这说明我的情况远不止于此问题。

Next, you’ll need a signing routine. If you find you have to implement one of these yourself I wish you all the best, because the specification is about as clear as mud. However, Mozilla have got utility libraries for Node.js and Python, at the very least. I’ll discuss the Python interface here.

接下来,您将需要一个签名例程。 如果您发现自己必须实现其中之一,则我祝您一切顺利,因为该规范与泥浆一样清晰。 但是,Mozilla至少有针对Node.js和Python的实用程序库。 我将在这里讨论Python接口。

You’ll first need to build your private key. To do that, you need the function browserid.jwt.load_key(). This takes two arguments. The first is a string indicating the type of key you’ve used: in our case, it’ll be RS256. The second is a dictionary of keys to values. Each key is one of the letters associated with the private key, as output by my helper script in the first section: e, n and d. Their values are the numerical values output by that same script.

首先,您需要构建私钥。 为此,您需要功能browserid.jwt.load_key() 。 这需要两个参数。 第一个是指示您使用的密钥类型的字符串:在我们的例子中,它将是RS256 。 第二个是键到值的字典。 每个密钥都是与私钥关联的字母之一,这是我的帮助脚本在第一部分中输出的: end 。 它们的值是同一脚本输出的数值。

The return value from this function is an object representing the private key. You’ll pass this into the signing function later.

该函数的返回值是一个代表私钥的对象。 稍后将其传递给签名功能。

Your next step is to build up the assertion dictionary. It’s easiest for me to show you how it looks in code:

下一步是建立断言字典。 对我来说,最容易向您展示代码中的外观:

{
    'iss': W,
    'exp': X,
    'iat': Y,
    'public-key': Z,
    'principal': {'email': A}
}
{
    'iss': W,
    'exp': X,
    'iat': Y,
    'public-key': Z,
    'principal': {'email': A}
}
 

In the above:

在上面:

  • W is a string representing the issuer. In this case, it should be the domain you’re signing for. For me, that was lukasa.co.uk.
  • X is a number representing the expiry date in milliseconds past the epoch. You should already have calculated this.
  • Y is a number representing the issuing date in milliseconds past the epoch. This is pretty easy for you to get.
  • Z is the user’s public key. This is a JSON object, and you must not use a string here, so make sure you called json.loads() on the POSTed data.
  • A is the email you’re signing for. Again, this was passed in.
  • W是代表发行者的字符串。 在这种情况下,它应该是您要注册的域。 对我来说,那是lukasa.co.uk
  • X是一个数字,表示到期日期之前的毫秒数。 您应该已经计算过了。
  • Y是一个数字,表示发行日期经过纪元后的毫秒数。 这很容易获得。
  • Z是用户的公钥。 这是一个JSON对象,您在此处不得使用字符串,因此请确保对POSTed数据调用了json.loads()。
  • A是您要签名的电子邮件。 同样,这是通过的。

With that created, you will want to call browserid.jwt.generate(). This takes two arguments: the first is the dict we just created, the second is the key object.

创建该代码后,您将需要调用browserid.jwt.generate() 。 这需要两个参数:第一个是我们刚刚创建的字典,第二个是键对象。

The returned data is the string form of the assertion, to be returned to the Javascript. You should simply return it back, with the Content-Type set to text/plain.

返回的数据是断言的字符串形式,将返回给Javascript。 您只需将Content-Type设置为text/plain ,将其返回即可。

My implementation of this is written for Django, and you can see it below, with added comments for clarity:

我的实现是为Django编写的,您可以在下面看到它,并添加了一些注释以使内容更加清楚:

Hopefully you can adapt this for use on your web framework.

希望您可以将其修改为在您的Web框架上使用。

全做完了! (All Done!)

See? It’s simple!

看到? 这很简单!

OK, not entirely simple, but fairly simple. Four steps, not too complicated, and you’re there. For most people it should be possible to implement all of this is an afternoon.

好的,不是完全简单,而是相当简单。 四个步骤,不太复杂,您就在那里。 对于大多数人来说,应该可以在下午完成所有这一切。

If you don’t have a useful Persona library, though, you might find this takes longer. You’ll need to read up on the spec for the signed assertion. It’s not entirely clear, but I think I’ve got my head around it. If you find you need help with it, I recommend dropping me a line on Twitter. If I can’t help you, I’ll point you to the real masters, like Dan Callahan.

但是,如果没有有用的Persona库,则可能会花费更长的时间。 您需要阅读有关签名断言的规范。 这还不是很清楚,但是我想我已经明白了。 如果您需要帮助,建议您在Twitter上留言。 如果我无法帮助您,我将带您了解真正的大师,例如Dan Callahan 。

翻译自: https://www.pybloggers.com/2013/04/writing-a-persona-identity-provider/

阿里云 身份提供商


http://www.niftyadmin.cn/n/982408.html

相关文章

用python设置背景音乐_用Python设置

用python设置背景音乐Perhaps you recall learning about sets and set theory at some point in your mathematical education. Maybe you even remember Venn diagrams: 也许您回想起在数学教育中某个时候对集合和集合理论的学习。 也许您甚至还记得维恩图&#xff1a; If t…

KVM 虚拟机在物理主机之间迁移的实现+好处

2011-09-02 22:20:33| 分类&#xff1a; 虚拟化云计|字号 订阅 前言 虚拟机的迁移技术为服务器的虚拟化提供简便的方法。目前流行的虚拟化产品 VMware&#xff0c;Xen&#xff0c;Hyper-V&#xff0c;KVM 都提供各自的迁移工具。其中 Linux 平台上开源的虚拟化工具 KVM 发展迅…

分享:nginx做代理IP端口转发

nginx做代理IP端口转发 http://my.oschina.net/kear/blog/109868

nginx禁止特定UA访问

一、UA是什么&#xff1f; User Agent 简称UA&#xff0c;就是用户代理。通常我们用浏览器访问网站&#xff0c;在网站的日志中&#xff0c;我们的浏览器就是一种UA。 二、禁止特定UA访问 最近有个网站&#xff08;www.C.com&#xff09;抄袭公司主站&#xff08;www.A.com&a…

python统计分布和概率_Python基本统计信息:概率

python统计分布和概率When studying statistics, you will inevitably have to learn about probability. It is easy lose yourself in the formulas and theory behind probability, but it has essential uses in both working and daily life. We’ve previously discussed…

Centos7下Rinetd安装与应用

Linux下做地址NAT有很多种方法。比如haproxy、nginx的4层代理&#xff0c;linux自带的iptables等都能实现。haproxy、nginx就不说了&#xff0c;配置相对简单&#xff1b;iptables配置复杂&#xff0c;概念也比较多DNAT、SNAT、PREROUTING、POSTROUTING等等。其实&#xff0c;L…

如何编写高质量的javascript代码

2019独角兽企业重金招聘Python工程师标准>>> 编写易维护的代码在软件开发中是非常重要的&#xff0c;不仅对完成软件项目十分有利&#xff0c;也有利于开发者和开发团队的其他人员的交流。有开发经验的人肯定会有这样的感觉&#xff0c;往往读代码比写代码更耗时间&…

python中的变量_Python中的变量

python中的变量In the previous tutorial on Basic Data Types in Python, you saw how values of various Python data types can be created. But so far, all the values shown have been literal or constant values: 在上一篇有关Python中的基本数据类型的教程中 &#xf…