简体   繁体   English

flask:如何桥接前端与后端服务以呈现 api 身份验证?

[英]flask: how to bridge front-end with back-end service to render api authentication?

In flask-restplus , I want to render API authentication view for my minimal flask API, where whenever when I make a request to the server, the first API should pop up a protective view for asking the user to provide customized token value before using API call. In flask-restplus , I want to render API authentication view for my minimal flask API, where whenever when I make a request to the server, the first API should pop up a protective view for asking the user to provide customized token value before using API称呼。 I came up my solution to make API authentication pop view before using api function, but couldn't get that correctly.在使用 api function 之前,我想出了我的解决方案来制作 API 身份验证弹出视图,但无法正确理解。 Can anyone help me out how to make my code work smooth?谁能帮助我如何使我的代码顺利运行? Any idea?任何想法?

My current attempt with full implementation :我目前的全面实施尝试

Here is the partial code of my implementation to do this task.这是我执行此任务的部分代码。

from functools import wraps
import requests, json, psycopg2, datetime
from time import time
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_restplus import Resource, Api, abort, fields, inputs, reqparse
from itsdangerous import SignatureExpired, JSONWebSignatureSerializer, BadSignature


class AuthenticationToken:
    def __init__(self, secret_key, expires_in):
        self.secret_key = secret_key
        self.expires_in = expires_in
        self.serializer = JSONWebSignatureSerializer(secret_key)

    def generate_token(self, username):
        info = {
            'username': username,
            'creation_time': time()
        }

        token = self.serializer.dumps(info)
        return token.decode()

    def validate_token(self, token):
        info = self.serializer.loads(token.encode())

        if time() - info['creation_time'] > self.expires_in:
            raise SignatureExpired("The Token has been expired; get a new token")

        return info['username']


SECRET_KEY = "f4b58245-6fd4-4bce-a8a4-27ca37370a3c"
expires_in = 600
auth = AuthenticationToken(SECRET_KEY, expires_in)

db = SQLAlchemy(app)

I pretty much coded up all for API authentication but couldn't get the authentication pop view that I expected in my desired output.我几乎为 API 身份验证编写了所有代码,但无法在我想要的 output 中获得我期望的身份验证弹出视图。

Update: output at server endpoint :更新:服务器端点的 output

When I tried http://127.0.0.1:5000/token at server endpoint, I got Not Found error.当我在服务器端点尝试http://127.0.0.1:5000/token时,出现Not Found错误。 How can I get my desired output?我怎样才能得到我想要的 output? any idea?任何想法?

I am wondering how can I get api protection view that requires a token to access API.我想知道如何获得需要令牌才能访问 API 的 api 保护视图。 currently, I have an error, couldn't get my desired output, so I am hopeful SO community helps me through with this.目前,我有一个错误,无法得到我想要的 output,所以我希望SO社区能帮助我解决这个问题。

desired output :所需的 output

I want to render a protective view for test API before using API call on the server endpoint.我想在服务器端点上使用 API 调用之前为测试 API 呈现保护视图。 Here is a mockup API authorization view that I want to get:这是我想要获得的模型 API 授权视图:

在此处输入图像描述

how can I make this happen using python flask, flask restful?如何使用 python flask、flask 来实现这一点? any thought?任何想法? thanks谢谢

As the comments suggest, there's no simple snippet of code anyone can share to answer this question.正如评论所暗示的那样,没有任何人可以分享任何简单的代码片段来回答这个问题。 You're basically asking for a five-part blog on how to attach a database to a Flask app in order to authenticate API credentials.您基本上是在要求一个由五部分组成的博客,介绍如何将数据库附加到 Flask 应用程序以验证 API 凭据。 I know it doesn't seem this way, but your questions really cascade from one topic Yinto the next.我知道看起来不是这样,但是您的问题确实从一个主题延伸到下一个主题。 I think your best bet is to look at the Flask Mega Tutorial Part IV Databases and Part V User Logins .我认为您最好的选择是查看 Flask Mega Tutorial Part IV Databases and Part V User Logins These tutorials cover the foundational concepts your code seems to be missing, as follows:这些教程涵盖了您的代码似乎缺少的基本概念,如下所示:

  1. Using SQLalchemy to define your database models使用SQLalchemy定义您的数据库模型
  2. Defining a basic authorization table in your DB在数据库中定义基本授权表
  3. Using encryption so that your authorization tokens can't be lifted from the database使用加密,以便您的授权令牌无法从数据库中提取
  4. Flushing expired tokens from the auth table从 auth 表中刷新过期令牌
  5. Using pre-built methods to validate authorization such as Flask-Github's github-callback example or Flask-Login's login_required decorator使用预构建的方法来验证授权,例如Flask-Github 的 github-callback 示例Flask-Login 的login_required 装饰器
  6. Using flask-SQLalchemy's create_db yo build the database from your model使用flask-SQLalchemy的create_db yo从你的model构建数据库
  7. Using flask-SQLalchemy's db.session to set/get data from the db使用 flask-SQLalchemy 的 db.session 从数据库设置/获取数据

For what it's worth, I really think The Flask Mega-Tutorial would be helpful.对于它的价值,我真的认为 Flask 超级教程会有所帮助。

UPDATE: Here is a minimal example using a dictionary as a toy database.更新:这是一个使用字典作为玩具数据库的最小示例 A few things about this example...关于这个例子的一些事情......

  1. If you run main.py and go to http://127.0.0.1:5000/token?username=admin&password=somepassword you will see the working get example如果您将 main.py 和 go 运行到http://127.0.0.1:5000/token?username=admin&password=somepassword您将看到有效的 get 示例

  2. If you go to http://127.0.0.1:5000 , click on "hello_world", click "post", and then click "try it out," you can enter a username and a password, and those will be added to the mock database.如果你是 go 到http://127.0.0.1:5000 ,点击“hello_world”,点击“post”,然后点击“try it out”,输入用户名和密码即可模拟数据库。

  3. After adding a username and password, you can go to http://127.0.0.1:5000/token?username=[]&password=[] except replace brackets with that new username and password.添加用户名和密码后,您可以 go 到http://127.0.0.1:5000/token?username=[]&password=[]除了用新的用户名和密码替换括号。 If you shutdown the server, the usernames and passwords won't be saved since it's just updating a dictionary.如果您关闭服务器,则不会保存用户名和密码,因为它只是在更新字典。

Hopefully, all this helps... once you've edited the app like this, it should be easier to debug issues that aren't related to username and password authentication.希望所有这些都对您有所帮助……一旦您像这样编辑了应用程序,应该更容易调试与用户名和密码身份验证无关的问题。

I figured what you are doing, and @B--rian, @Matt L. mentioned above, it is not one-shot task that SO community would give help all code base.我知道你在做什么,上面提到的@B--rian,@Matt L., SO社区不会为所有代码库提供帮助,这不是一次性的任务。 But this is what I did and I would guide you how you can finish you task with some pains.但这就是我所做的,我会指导你如何用一些痛苦来完成你的任务。

we can try to solve this task by dividing several steps, but let's come around this step, I am gonna post nest one shortly:我们可以尝试通过分几个步骤来解决这个任务,但是让我们绕过这一步,我很快就会发布嵌套一个:

from functools import wraps
from time import time
from flask import Flask
from flask import request
from flask_restplus import Resource, Api
from flask_restplus import abort
from flask_restplus import fields
from flask_restplus import inputs
from flask_restplus import reqparse
from psycopg2.extensions import AsIs
import psycopg2, datetime, requests, json
from itsdangerous import SignatureExpired, JSONWebSignatureSerializer, BadSignature

'''
## to get API security key, to do:
import uuid
print(str(uuid.uuid4()))
'''

class AuthenticationToken(object):
    def __init__(self, secret_key, expires_in):
        self.secret_key = secret_key
        self.expires_in = expires_in
        self.serializer = JSONWebSignatureSerializer(secret_key)

    def generate_token(self, username):
        info = {
            'username': username,
            'creation_time': time()
        }
        token = self.serializer.dumps(info)
        return token.decode()

    def validate_token(self, token):
        info = self.serializer.loads(token.encode())

        if time() - info['creation_time'] > self.expires_in:
            raise SignatureExpired("The Token has been expired; get a new token")

        return info['username']

SECRET_KEY = "f4b58245-6fd4-4bce-a8a4-27ca37370a3c"
expires_in = 600
auth = AuthenticationToken(SECRET_KEY, expires_in)

app = Flask(__name__)
api = Api(app,authorizations={
                'API-KEY': {
                    'type': 'apiKey',
                    'in': 'header',
                    'name': 'AUTH-TOKEN'
                }
            },
          security='API-KEY',
          default="API AUTH TOKEN", 
          title="immunoMatch RESTful API", 
          description="Immunomatch ED API Authentication View") 

db = psycopg2.connect(database='test_db', user='postgres', password='password', host='localhost', port="5432")

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):

        token = request.headers.get('AUTH-TOKEN')
        if not token:
            abort(401, 'Authentication token is missing')

        try:
            user = auth.validate_token(token)
        except SignatureExpired as e:
            abort(401, e.message)
        except BadSignature as e:
            abort(401, e.message)

        return f(*args, **kwargs)
    return decorated

credential_model = api.model('credential', {
    'username': fields.String(required=True),
    'password': fields.String(required=True)
})

credential_parser = reqparse.RequestParser()
credential_parser.add_argument('username', type=str)
credential_parser.add_argument('password', type=str)

@api.route('/token')
class Token(Resource):
    @api.expect(credential_parser, validate=True)
    def get(self):
        args = credential_parser.parse_args()
        username = args.get('username')
        password = args.get('password')
        cursor = db.cursor()
        cursor.execute('SELECT * FROM public.authorized_user_table')
        user = cursor.fetchone()

        if username != user[1]:
            api.abort(404, "Username: {} doesn't exist".format(username))
        if password != user[2]:
            api.abort(401, "Wrong password")
        return {"token": auth.generate_token(username)}

if __name__ == '__main__':
    db = psycopg2.connect(database='test_db', user='postgres', password='password', host='localhost', port="5432")
    app.run(debug=True)

I suggest try to digest what @B--rian, @Matt L mentioned in their post, I think @Matt L did point out something worthy you should think of, their advice is nice except not much coding attempt was given.我建议尝试消化@B--rian、@Matt L 在他们的帖子中提到的内容,我认为@Matt L 确实指出了一些值得你考虑的东西,他们的建议很好,只是没有给出太多的编码尝试。 but I would come back to you as soon as I figured out rest.但是一旦我发现 rest,我会尽快回复你。

If I understand you correctly, you want to implement a token based implementation similar to JWT authorization .如果我理解正确,您希望实现类似于JWT 授权的基于令牌的实现。

JWT in general JWT 一般

What JWT is all about is nicely summarized at docs.nginx.com > JWT authorization which says JWT 的全部内容在文档中得到了很好的总结。nginx.com > Z1D1FADBD9150349C135781 授权

JWT is data format for user information in the OpenID Connect standard, which is the standard identity layer on top of the OAuth 2.0 protocol. JWT 是 OpenID Connect 标准中的用户信息数据格式,它是 OAuth 2.0 协议之上的标准身份层。 Deployers of APIs and microservices are also turning to the JWT standard for its simplicity and flexibility. API 和微服务的部署者也开始转向 JWT 标准,因为它的简单性和灵活性。 With JWT authentication, a client provides a JSON Web Token, and the token will be validated against a local key file or a remote service.通过 JWT 身份验证,客户端提供 JSON Web 令牌,该令牌将针对本地密钥文件或远程服务进行验证。

You do not necessarily have to do the JWT on webserver level, but I usually prefer to do authentication on web server level in order to nicely split data (aka endpoints) and security.您不一定必须在网络服务器级别执行 JWT,但我通常更喜欢在 web 服务器级别进行身份验证,以便很好地拆分数据(又名端点)和安全性。

Let us now focus on JWT with flask.现在让我们关注 JWT 和 flask。 The blog.teclacode.com nicely summarizes the work flow [comments by me]: blog.teclacode.com很好地总结了工作流程 [我的评论]:

  1. User provides their username and password [to an extraordinary endpoint]用户提供他们的用户名和密码 [给一个非凡的端点]

[The next 3 steps happen within that extraordinary endpoint]. [接下来的 3 个步骤发生在那个非凡的端点内]。

  1. We verify they are correct inside our Flask app我们在 Flask 应用程序中验证它们是正确的
  2. We generate a JWT which contains the user's ID.我们生成一个包含用户 ID 的 JWT。
  3. We send that to the user.我们将其发送给用户。
  4. Whenever the user makes a request to our application, they must send us the JWT we generated earlier.每当用户向我们的应用程序发出请求时,他们必须向我们发送我们之前生成的 JWT。

I guess your challenge spans steps 1 through 4. Step 5 you seem to have implemented already: It is the un-secured endpoint(s) plus an additional, mandatory POST-parameter token .我猜你的挑战跨越了第 1 步到第 4 步。第 5 步你似乎已经实现了:它是不安全的端点加上一个额外的、强制性的 POST-parameter token Those endpoints will give back data to the client once a valid token is provided.一旦提供了有效的令牌,这些端点就会将数据返回给客户端。 In other words: Each ordinary endpoint calls a function which validates a given token.换句话说:每个普通端点都会调用一个 function 来验证给定的令牌。 Is this understanding correct?这种理解正确吗? Is step 5 already working for you?第 5 步已经为您工作了吗?

I never implemented JWT with python, but a quick search for packages with JWT in their names give me the impressions that there are many ready-to-use token generators out there you "just" have to configure.我从未使用 python 实现 JWT,但是快速搜索名称中带有 JWT 的软件包给我的印象是,有许多即用型令牌生成器可供您“刚刚”配置。

Dive into your code深入了解您的代码

A couple of questions about your code:关于您的代码的几个问题:

  1. Do you really want to hard-code your secret key inside your code?你真的想在你的代码中硬编码你的密钥吗? That is a major security flaw, since you will have key disclosed in all your GIT commits etc.这是一个重大的安全漏洞,因为您将在所有 GIT 提交等中公开密钥。
  2. Your entire code at GIT is not easy to access since there are some syntax errors in some files which prevent a full text search, the error message reads:您在 GIT 的整个代码不容易访问,因为某些文件中存在一些语法错误,会阻止全文搜索,错误消息显示:

    We can make that file beautiful and searchable if some errors are corrected.如果某些错误得到纠正,我们可以使该文件美观且可搜索。

  3. Where and how is user_db defined? user_db在哪里以及如何定义? You mentioned that in a comment, but I cannot find that elsewhere.您在评论中提到了这一点,但我在其他地方找不到。 It would be helpful to have a paragraph on this in your question.在您的问题中有一段关于此的段落会很有帮助。
  4. I guess that http://127.0.0.1:5000/token is an endpoint which provides the token, but I do not fully understand from the question in its current form where the endpoint /token is implemented.我猜想http://127.0.0.1:5000/token是一个提供令牌的端点,但我从当前形式的问题中不完全理解端点/token的实现位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 后端通过Flask中的Ajax从前端接收JSON之后的渲染模板 - Render template after back-end receive JSON from front-end via Ajax in Flask 如何将 Flask 后端应用程序连接到 Flask 前端应用程序? - How to connect a Flask Back-end app to Flask Front-end app? 如何使用 WebSocket 从前端 Javascript 发送图像到后端 Flask? - How to send and image from Javascript front-end to Flask back-end with WebSocket? 如何检测用户何时关闭 flask 后端而不是前端的浏览器/选项卡(如果不是有效的 javascript) - How to detect when a user Close browser/tab in flask back-end not in front-end (if not valid javascript) 如何连接前端和后端? - How can I connect front-end with back-end? 如何使用rest API连接AngularJS前端和Python后端? - How to connect AngularJS front-end and Python back-end using rest API? 如何从前端JavaScript发送JSON数据到使用Python搭建的后端Server Flask - How to send JSON data from front-end JavaScript to the Back-end Server built by using Python Flask Python Flask - 在后端处理从前端获得的输入 - Python Flask - Processing input obtained from front-end in the back-end Python(后端)和 Kotlin(前端)为 Android - Python (back-end) and Kotlin (front-end) for Android 匹配密码与前端或后端? - Matching Passwords with Front-End or Back-End?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM