如何使用 Django 测试 https 连接,就像使用‘ runserver’测试非 https 连接一样容易?

我有一个使用“安全”cookie 的应用程序,并且想要测试它的功能,而不需要设置一个复杂的支持 SSL 的开发服务器。有没有什么简单的方法可以使用 ./manage.py runserver测试未加密的请求?

84259 次浏览

它不像内置的开发服务器那样简单,但是使用 stunnel 作为浏览器和开发服务器之间的 SSLify 中间人并不太困难。Stunnel 允许您在机器上设置一个轻量级服务器,该服务器接受已配置端口上的连接,用 SSL 封装它们,并将它们传递给其他服务器。我们将使用它来打开一个 stunnel 端口(8443) ,并将它接收到的任何通信量传递给 Django runserver 实例。

首先,你需要一个可以是 下载到这里或者由你的平台的软件包系统提供的 stunnel (例如: apt-get install stunnel)。我将使用第4版的 unnel (例如: Ubuntu 上的 /usr/bin/stunnel4) ,第3版也可以工作,但是有不同的配置选项。

首先在 Django 项目中创建一个目录,以保存必要的配置文件和 SSLish 内容。

mkdir stunnel
cd stunnel

接下来,我们需要创建用于 SSL 通信的本地证书和密钥。为此,我们转向开放。

创建密钥:

openssl genrsa 2048 > stunnel.key

创建使用这个密钥的证书(这会要求您提供证书中包含的大量信息——只要回答您觉得合适的问题就可以了) :

openssl req -new -x509 -nodes -sha1 -days 365 -key stunnel.key > stunnel.cert

现在,将这些文件合并成一个文件,该文件将用于其 SSL 通信:

cat stunnel.key stunnel.cert > stunnel.pem

为 stunnel 创建一个名为 dev _ https 的配置文件,其内容如下:

pid=


cert = stunnel/stunnel.pem
sslVersion = SSLv3
foreground = yes
output = stunnel.log


[https]
accept=8443
connect=8001
TIMEOUTclose=1

这个文件告诉 stunnel 它需要知道什么。具体来说,您告诉它不要使用 pid 文件,证书文件在哪里,使用什么版本的 SSL,它应该在前台运行,它应该在哪里记录它的输出,它应该接受端口8443上的连接,并将它们穿梭到端口8001。最后一个参数(TIMEOUTclose)告诉它在没有活动的情况下传递1秒钟后自动关闭连接。

现在弹出到您的 Django 项目目录(其中包含 manage.py) :

cd ..

在这里,我们将创建一个名为 runserver 的脚本,它将运行 tunnel 和两个 django 开发服务器(一个用于普通连接,一个用于 SSL 连接) :

stunnel4 stunnel/dev_https &
python manage.py runserver&
HTTPS=1 python manage.py runserver 8001

我们一行一行来分析一下:

  • 第1行: 启动 stunnel 并将其指向我们刚刚创建的配置文件。这样就有了在端口8443上侦听、封装它在 SSL 中接收到的所有连接并将它们传递到端口8001的途径
  • 第2行: 启动一个正常的 Django runserver 实例(在端口8000上)
  • 第3行: 启动另一个 Django runserver 实例(在端口8001上) ,并将其配置为将所有传入连接视为正在使用 HTTPS 执行的连接。

让我们刚刚创建的 runscript 文件可执行:

chmod a+x runserver

现在,当您想要运行开发服务器时,只需在项目目录中执行 ./runserver。要尝试使用它,只需将浏览器指向正常的 HTTP 通信 HTTP://localhost:8000,以及 HTtPS 通信 HTTPS://localhost:8443。请注意,您的浏览器几乎肯定会抱怨所使用的证书,并要求您添加异常或以其他方式显式指示浏览器继续浏览。这是因为您创建了自己的证书,而浏览器并不信任它能够说出证书的真实身份。这对于开发来说是很好的,但是对于生产来说显然是不够的。

不幸的是,在我的机器上,当我按下 Ctrl-C 时,这个运行服务器脚本不能很好地退出。我必须手动关闭进程——有人有什么建议来修复这个问题吗?

感谢迈克尔吉尔的 邮寄和姜戈编织的 维基条目的参考材料。

它可以与 socat 一起用一行代码完成:

socat openssl-listen:8443,fork,reuseaddr,cert=server.pem,verify=0 tcp:localhost:8000

其中,8443是一个监听传入 HTTPS 连接的端口,server.pem 是一个自签名的服务器证书,localhost: 8000是一个像往常一样启动的调试 HTTP 服务器。

更多细节: http://www.dest-unreach.org/socat/doc/socat-openssltunnel.html

注册到 https://ngrok.com/。您可以使用 https 进行测试。这可能会帮助那些只想快速测试 https 的人。

我建议使用 Django-sslserver包。

PyPI 上的当前包只支持 Django 版本1.5.5,但是已经通过 5d4664c提交了一个补丁。有了这个修复程序,系统运行良好,是一个非常简单和直接的测试 https 连接的解决方案。

更新: 自从我发布了我的答案之后,上面的提交已经被 合并了提交到主分支,并且一个新的 释放已经被推送到 PyPI。因此,不需要为该特定补丁指定5d4664c 提交。

对于那些为了调试目的而寻找 stunnel 选项的前景化版本的人:

Pem 是在 Evan Grimm 的最佳答案中生成的证书。

监听端口443上的所有本地接口,然后转发到本地主机上的端口80

sudo stunnel -f -p stunnel.pem -P ~/stunnel.pid -r localhost:80 -d 443

Sudo 仅对1024以下的传入端口(- d [ host: ]端口)是必需的

与 django-sslserver 类似,您可以使用 姜戈接发中的 RunServerPlus

它依赖于 Werkzeug (因此您可以访问优秀的 Werkzeug 调试器)和 pyOpenSSL (仅在 ssl 模式下需要) ,所以要安装 run:

pip install django-extensions Werkzeug pyOpenSSL

将其添加到项目 setings.py 文件中的 INSTALLED _ APPS:

INSTALLED_APPS = (
...
'django_extensions',
...
)

然后您可以使用以下命令在 ssl 模式下运行服务器:

./manage.py runserver_plus --cert /tmp/cert

这将在 /tmp/cert.crt创建一个证书文件,在 /tmp/cert.key创建一个密钥文件,然后可以在以后的会话中重用这个文件。

Django 扩展中包含了大量额外的内容,您可能会发现它们很有用,因此很值得快速浏览一下文档。

安装就行了

sudo pip install django-sslserver

在已安装的应用程序中包含 sslserver

INSTALLED_APPS = (...
"sslserver",
...
)

现在你可以跑了

 python manage.py runsslserver 0.0.0.0:8888

使用 Nginx 而不是 Django 等代理处理 SSL/TLS。可以将 Nginx 设置为侦听端口443,然后将请求转发到 Django dev 服务器(通常是 http://127.0.0.1:8000)。Nginx 的配置如下所示:

server {
listen 443 ssl;
server_name django-dev.localhost;


ssl_certificate /etc/ssl/certs/nginx_chain.pem;
ssl_certificate_key /etc/ssl/private/nginx.pem;


location / {
proxy_pass http://127.0.0.1:8000/;
proxy_set_header Host $host;
}
}

您还需要将 django-dev.localhost映射到 127.0.0.1,并在 settings.py中将 django-dev.localhost添加到 ALLOWED_HOSTS。在 Linux 上,您需要向 /etc/hosts添加以下代码行:

127.0.0.1   django-dev.localhost

然后你就可以通过浏览器中的 https://django-dev.localhost访问你的开发站点(你需要绕过浏览器的安全警告)。

一种方法是使用 ngrok。

  1. 安装 ngrok。 下载链接: < a href = “ https://ngrok.com/download”rel = “ norefrer”> https://ngrok.com/download

  2. 在终端上发出下面的命令

    ngrok http 8000
    

它将开始 ngrok 会议。它会列出两个网址。一个被映射到 http://localhost:8000。第二个被映射为 https://localhost:8000。请查看下面的截图。使用任一个 url。它将映射到您的本地服务器。

sample ngrok session screenshot


第二种方法是根据@Evangrim. Link-https://stackoverflow.com/a/8025645/9384511提供的解决方案

如果需要多次测试 https url,则首选第一种方法。但是如果您的需求说 https 是强制性的,那么最好采用第二种方法。 我遇到了以下问题@Evangrim 的解决方案

  1. 当通过请求访问 https url 时,您将获得 subAltNames 错过警告。这应该不是一个问题。您的网址应该工作 好吧。
  2. 您将无法通过 android volley 访问 https url 抛出错误-您的 ip 或 localhost 未被验证。类似 那个。

我通过向 ssl 证书添加 subAltname 解决了这些问题。下面是我在没有上述两个问题的情况下运行本地 https 服务器所遵循的步骤。

  1. 在 ubuntu 执行时,使用的版本是 version 4(/usr/bin/tunnel4)

     sudo apt-get install stunnel
    
  2. 在 Django 项目中创建目录 stunnel。

  3. 将 openssl.cnf 从/etc/ssl/openssl.cnf 或/usr/lib/ssl/openssl.cnf 复制到 stunnel。

    1. 在 req 区域集中搜索[ req ]或[ req ]区域

       x509_extensions = v3_ca
      req_extensions = v3_req
      

      应该出现 x509 _  和 req _  插件。如果沒有,就添加它。如果有評論,就取消評論。

    2. 搜索[ v3 _ req ]部分

       subjectAltName = @alt_names
      
    3. 搜索[ v3 _ ca ]部分

       subjectAltName = @alt_names
      
    4. 我们需要添加一个新的 alt _ names. 这可以在任何地方添加。 对于本地主机和您的 IP 地址添加如下:

       [alt_names]
      DNS.1 = localhost
      IP.1 = x.x.x.x
      IP.2 = 127.0.0.1
      
  4. 创建键执行

     openssl genrsa 1024 > stunnel.key
    
  5. 要创建 DER 编码的证书,请执行第一个或第二个命令

    1. 如果您直接编辑了/etc/ssl/openssl.cnf 或/usr/lib/ssl/openssl.cnf

       openssl req -new -x509 -nodes -sha1 -days 365 -key stunnel.key > stunnel.cert
      
    2. 如果您已经将 openssl.cnf 复制到 stunnel 目录中。

       openssl req -new -x509 -nodes -sha256 -days 365 -key stunnel.key -config openssl.cnf > stunnel.cert
      
  6. 创建 PEM 编码的证书执行。

     cat stunnel.key stunnel.cert > stunnel.pem
    
  7. 创建一个名为 dev _ https. config 的混乱状态配置文件,其中包含以下内容。

     #not to use pid
    pid=
    #path of certificate file
    cert = stunnel/stunnel.pem
    #version of SSL to use
    sslVersion = TLSv1.1
    foreground = yes
    #log output path
    output = stunnel.log
    
    
    [https]
    #listen on port 8443
    accept=8443
    #tunnel the connection to port 8001
    connect=8001
    # and close connection automatically after one second
    TIMEOUTclose=1
    

    请注意,sslVersion 基于 python 解释器使用的 OpenSSL 版本。我的是 OpenSSL1.1.1。对于这个版本,请使用 TLSv1.1。找出你的 openssl 版本,并添加相应的版本。 您可以通过执行以下命令来查找 OpenSSL 版本

     python -c "import ssl; print(ssl.OPENSSL_VERSION)"
    
  8. 创建一个名为 runserver 的脚本并添加以下内容 这个脚本将运行两个 stunnel 和两个 django 环境,一个用于 http 连接,一个用于 https 连接。 运行服务器不能完全退出的问题(正如 Evan 所提到的)通过调用清理函数的 trap 语句来解决。

     stunnel4 stunnel/dev_https.config &
    stunnel_pid=$!
    echo "stunnel pid: $stunnel_pid"
    python manage.py runserver 0.0.0.0:8000 &
    py_server_bg=$!
    echo "BG py server pid: $py_server_bg"
    HTTPS=1 python manage.py runserver 0.0.0.0:8001
    
    
    function cleanup()
    {
    echo "Cleaning up"
    kill $stunnel_pid
    kill $py_server_bg
    echo "Cleaned"
    }
    
    
    trap cleanup EXIT
    
  9. 执行

     chmod a+x runserver
    
  10. 从您的 django 项目执行

    ./runserver
    
  11. 现在,如果执行 python 请求命令,则不会显示 subjectAltNames 缺少警告。此外,您的安卓截击 https 请求将执行良好。

    data = {'login_id':'someid'}
    headers = {'content-type': 'application/json'}
    url = "https://localhost:8443/myauth/check-id/"
    r=requests.post(url, data=json.dumps(data), headers=headers,verify='stunnel/stunnel.pem')
    

我要感谢@伊万格里姆提供的解决方案。感谢“ Friek”、“ Utku”和“ dr”。西布伦。根据他们的意见,我实现了清理功能。

转到 Sslforfree.com下载证书文件

download 3246328748324897329847324234.txt file for verify

您需要将其添加到 views.py 中

from django.http import HttpResponse


def read_file(request):
f = open('7263713672163767218367687767.txt', 'r')
file_content = f.read()
f.close()
return HttpResponse(file_content, content_type="text/plain")

之后 加

urls.py

    path('.well-known/pki-validation/', read_file)

安装

pip install django-extensions
pip install pyOpenSSL
pip install werkzeug


./manage.py runserver_plus 0:443 --cert-file certificate.crt  --key-file private.key

我有一个非常简单的解决方案,只需按照下面的步骤,您就可以在 https://security 服务器上运行您的 django 项目/应用程序。

步骤1在系统上安装 openssl。您可以在 https://slproweb.com/products/Win32OpenSSL.html上获得用于安装的可执行文件。我建议您安装 Win64 OpenSSL v1.1.1 L (版本) ,与3.0.0相比,它是稳定的

成功安装后,请执行步骤2导航到示例中的 C: Program Files OpenSSL-Win64 bin。您可以导航到您安装了 openssl 的安装目录,在这里您可以找到 openssl.exe 作为管理员运行它。

步骤3输入以下命令开始生成证书和私钥: Req-x509-sha256-node-days 365-newkey rsa: 2048-keyout PrivateKey.key-out cericate.crt

步骤4: 然后系统会提示您输入适用的专有名称(DN)信息,共有七个字段,如下面的屏幕截图所示: enter image description here

步骤5: 完成之后,您将找到在 OpenSSL bin 目录下创建的证书.crt 和 private ateKey.key 文件,如下所示 enter image description here

步骤6: 在系统中安装 django-sslserver。

Stpe 7: 在 IDLE 终端上执行以下命令: Py runsslserver ——证书 C:/Program Files/OpenSSL-Win64/bin/cer Crt —— key C:/Program Files/OpenSSL-Win64/bin/private ateKey.key 127.0.0.1:8000

恭喜您现在的 django 项目在 https 上运行。

我希望这会对你有所帮助,如果你遇到任何困难,请尽管问。

尝试向 这个答案添加注释,他询问关于终止进程的问题,但注释中的代码格式有限。这是我修改后的 runserver.bash

#!/bin/bash


# Script to kill previous instances of dev servers,
# then start the 2 dev servers, one of which is https.
#


cd "$(dirname "$0")"
echo $PWD


. bin/activate


if [[ "$1" =~ "kill" ]]; then
if [[ -n `ps -ef | egrep 'python manage.py runserver [1-9,\.]*' | grep -v grep | awk '{ print $2}'`  ]]; then
kill -15 `ps -ef | egrep 'python manage.py runserver [1-9,\.]*' | grep -v grep | awk '{ print $2}'`
fi
else
if [[ -n `ps -ef | egrep 'python manage.py runserver [1-9,\.]*' | grep -v grep | awk '{ print $2}'`  ]]; then
kill -15 `ps -ef | egrep 'python manage.py runserver [1-9,\.]*' | grep -v grep | awk '{ print $2}'`
fi
if [[ -z `ps -ef | egrep 'stunnel4 stunnel/dev_https' | grep -v grep | awk '{ print $2}'` ]]; then
stunnel4 stunnel/dev_https &
fi
python manage.py runserver 192.168.1.183:8006 &
HTTPS=1 python manage.py runserver 192.168.1.183:8001
fi

基于 Ryabchenko Alexander 的回答,我在 Django 进行局部测试时是这样做的:

token_url = '{}/o/token/'.format(settings.BOQ_APP_URL)
oauth = OAuth2Session(
client=LegacyApplicationClient(client_id=settings.BOQ_APP_CLIENT_ID),
)


# disable cert verification for local tests
if settings.DEBUG:
oauth.verify = False


token = oauth.fetch_token(
token_url=token_url,
username=settings.BOQ_APP_USERNAME,
password=settings.BOQ_APP_PASSWORD,
client_secret=settings.BOQ_APP_CLIENT_SECRET,
)


print(token)