前几天用 Tornado 框架写了一个服务, 微信小程序需要去连接它. http很简单
但是微信小程序强制要求用到https 悲剧就开始了.
https 证书是从阿里云上下载下来的, crt证书
我的证书是从阿里云下载的免费证书, Tornado 支持的好像是apache的证书 crt结尾的.
理论上证书应该没什么问题. 搞上去以后发现用浏览器去访问的时候 https能正常访问的但是postman和微信小程序开发工具不支持.
我的错误代码如下
#完整打分流程V2
import os
import os.path
# import sys
# sys.path.append('../')
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.autoreload
import cv2
import json
from handlers import OnlineRecognition, XiuZheng #这里的AA是文件名
# import handlers
from tornado.options import define, options
# import ssl
# ctx = ssl.SSLContext()
# ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
# ctx.options &= ~ssl.OP_NO_TLSv1_1
# ctx.options &= ~ssl.OP_NO_TLSv1_2
# ctx.options &= ~ssl.OP_NO_SSLv2
# ctx.options &= ~ssl.OP_NO_SSLv3
# ctx.options &= ~ssl.OP_NO_SSLv3
# ctx.options &= ~ssl.OP_NO_SSLv3
# ctx.options = ssl.OP_ALL
# ctx.options |= ssl.OP_NO_TLSv1_1
# ctx.options = ssl.OP_NO_TLSv1_2
# ctx.options |= ssl.OP_NO_SSLv2
# ctx.options |= ssl.OP_NO_SSLv3
import ssl
# context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
# context.load_cert_chain('PATH_TO_PUBLIC_KEY','PATH_TO_PRIVATE_KEY')
# print(context.options)
# 定义端口用于指定HTTP服务监听的端口
# 如果命令行中带有port同名参数则会称为全局tornado.options的属性,若没有则使用define定义。
define("port", type=int, default=443, help="run on the given port")
# 调试模式
define("debug", type=bool, default=True, help="debug mode")
# windows 系统下 tornado 使用 使用 SelectorEventLoop 解决一个bug start
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# windows 系统下 tornado 使用 使用 SelectorEventLoop 解决一个bug end
static_path = os.path.join(os.path.dirname(__file__), 'static')
static_path = static_path.replace("\\","/")
crtpath = os.getcwd()+ "/https/**.**.com_public.crt"
keypath = os.getcwd()+ "/https/**.**.com.key"
#print(crtpath,keypath)
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application(
handlers=[
(r'/', OnlineRecognition.OnlineRecognitionHandler)
],
template_path=os.path.join(os.path.dirname(__file__), "templates"),
settings = {
'static_path' : static_path ,'debug' : True, "compiled_template_cache":False },
static_path =static_path
)
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) # 这里自己控制ssl
# ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) #这样初始化强制使用ssl的加密版本是TLSV1_2 , 结果试了也不行.
ssl_ctx.load_cert_chain(crtpath, keypath) #自己控制ssl的选项
http_server = tornado.httpserver.HTTPServer(app,
ssl_options = ssl_ctx
# ssl_options={
# "certfile": crtpath,
# "keyfile": keypath
# }
)
print("web 服务正在监听",options.port)
http_server.listen(options.port)
print("web 服务已启动...")
# tornado.ioloop.IOLoop.instance().start()
instance = tornado.ioloop.IOLoop.instance()
tornado.autoreload.start(5)
instance.start()
但是用postman和微信小程序开发工具就提示错误. 我就郁闷了…
提示报错如下
SSL Error: Unable to verify the first certificate
openssl s_client -connect www.abaefew.com:443 #这里是我的域名.
返回错误
后来查看了下交换信息, 感觉应该是第4,5,6这几步可能出错了.
但是不知道为什么…云里雾里的.
后来搜到了这篇文章
https://www.cnblogs.com/Kempff/p/12631172.html
看他的代码,里面用到了第三个证书, 于是我突然间想到我从阿里云上下载的是3个文件呢.
一个叫 chain.crt 好像一直没用过. 于是我就用它试了一下…
居然神奇的成功了.
..compublic.crt是公钥, 应该是第二步发给客户端的证书.
..com.key应该是秘钥, 用来解密的. 要保存好.
..com_chain.crt应该是根证书, 估计跟阿里云有关系吧… 瞎猜的, 有知道的麻烦说一下.
然后我的代码改成下面这样就可以了.
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=chainpath)
cafile参数必须指向 ..com_chain.crt 也叫CA证书
#完整打分流程V2
import os
import os.path
# import sys
# sys.path.append('../')
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.autoreload
import cv2
import json
import ssl
from handlers import OnlineRecognition, XiuZheng #这里的AA是文件名
from tornado.options import define, options
# print(context.options)
# 定义端口用于指定HTTP服务监听的端口
# 如果命令行中带有port同名参数则会称为全局tornado.options的属性,若没有则使用define定义。
define("port", type=int, default=443, help="run on the given port")
# 调试模式
define("debug", type=bool, default=True, help="debug mode")
# windows 系统下 tornado 使用 使用 SelectorEventLoop 解决一个bug start
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# windows 系统下 tornado 使用 使用 SelectorEventLoop 解决一个bug end
static_path = os.path.join(os.path.dirname(__file__), 'static')
static_path = static_path.replace("\\","/")
chainpath = os.getcwd()+ "/https/ai.px82.com_chain.crt"
crtpath = os.getcwd()+ "/https/ai.px82.com_public.crt"
keypath = os.getcwd()+ "/https/ai.px82.com.key"
#print(crtpath,keypath)
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application(
handlers=[
(r'/', OnlineRecognition.OnlineRecognitionHandler),
(r'/OnlineRecognition', OnlineRecognition.OnlineRecognitionHandler),
(r'/XiuZheng', XiuZheng.XiuZhengHandler),#修整AI算法的识别名称
],
template_path=os.path.join(os.path.dirname(__file__), "templates"),
settings = {
'static_path' : static_path ,'debug' : True, "compiled_template_cache":False },
static_path =static_path
)
# cafile 这个参数是必须得. 网上的都没有提起过.
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=chainpath)
# ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) #这种是强制使用TLSV1.2协议
# ssl_ctx.verify_mode = ssl.CERT_NONE 不管客户端怎样都不断开连接, 好像没啥用.
# ssl_ctx.check_hostname =False 不做域名一致验证
ssl_ctx.load_cert_chain(crtpath, keypath)
http_server = tornado.httpserver.HTTPServer(app,
ssl_options = ssl_ctx #下面的这种写法不能用.
# ssl_options={
# "certfile": crtpath,
# "keyfile": keypath
# }
)
print("web 服务正在监听",options.port)
http_server.listen(options.port)
print("web 服务已启动...")
# tornado.ioloop.IOLoop.instance().start()
instance = tornado.ioloop.IOLoop.instance()
tornado.autoreload.start(5)
instance.start()
总结一下, 对ssl加密这套流程不是很懂. 导致的一个失误, 网上给的教程里面也多数没有写关于这个的说明.