前后端分离djangorestframework—— 在线视频平台接入第三方加密防盗录视频
加密视频
在以后的开发项目中,很可能有做在线视频的,而在线视频就有个问题,因为在线播放,就很有可能视频数据被抓包,如果这个在线视频平台有付费视频的话,这样就会有人做点倒卖视频的生意了,针对这个问题,目前国内有很多不错的加密视频平台,可以把你平台的视频放在他们那里,然后通过他们的机制进行加密,然后做一套机制,当用户使用平台播放时,其实是平台去加密视频方请求过来的加密视频,这样就可以保证视频的安全性了
常用加密视频平台
鄙人听说的,有保利威,金盾,还有很多很多我叫不上名,搜索引擎一搜就有一大堆的,本次教程说的是保利威
保利威
官网:传送门 有直播和点播的服务,直播是什么不用多说了吧,点播的意思就是我上面说的,把录制好的视频放到平台那种
本教程只介绍使用云点播功能
文档手册
点击使用手册的云点播:使用手册文档:传送门
选到h5视频播放:当然你也可以使用flash,但是现在的平台基本都用的是h5了,所以本次选用h5的
官方文档案例演示
复制这段代码放到一个html文档里,直接点那个浏览器标识打开测试:
这个页面整个都是保利威给我我们提供的,可以加速播放,可以查看视频参数,可以看列表,然后还有个【test】这个就是跑马灯,相信有过购买视频的朋友都知道,你看你的付费视频时,都有自己的用户名啥的,假如你自己私自录制,传出去,就是用这个跑马灯就可以追踪到你的,他官方给的实例就是这样
但其实,因为我电脑装了IDM工具,且它这个视频只是展示,还没有真正的加密(看到后面的你就知道为什么没有真正的加密了),所以我的IDM自动嗅探视频地址
且还支持持断点播放
但是这个由于是案例,所以利用IDM直接就可以下载,且播放时还没有水印:
好的,演示的视频就是这样了。
代码实现
前期准备:
你需要注册一个保利威的账号,然后拿到id和secret,点设置 - API接口:
注意id和secretkey以及token,这里的token有读的和写的,后期会用到
上传视频
上传一个测试的视频,上传的时候设置加密参数
上传之前开启加密设置
把加密设置打开,然后下面的移动端加密按你自己的需求来,我这里暂且不设置
相关的更多的视频配置参数:传送门
好的,我这里开始上传,我上传了一首歌的MV:
上传完之后平台自动解码,然后就会有一个视频加密的id了:
转码之后,审核:
在审核的时候你就可以做一些相关的视频设置:
这里还有很多的设置,不一一展示,自己体验了
等待大概一两分钟之后,显示已发布,表示可以用了:
其他参数配置
这里还有一些设置,比如播放域名设置,这些就自己去体验了,我这里这些都直接用默认的
查看官方文档:
看文档得知有两个步骤:
- 服务端获取token,将token给客户端
- 客户端拿到token,开始播放视频
获取token
他官方文档给的是php的,不存在,后面重构成Python的就行了
播放:
播放有两种播放形式,一种是直接在playsafe里传一个token,一种是给一个函数,函数必须带视频id和next,next是一个函数,在获取token之后将token传到next里即可,而这官方给的文档是next(playsafe),这里有个坑,不是传playsafe,而是传token,我在这卡了很久
再次强调:视频如果是加密的,需要设置加密参数 playsafe,playsafe有两种形式,一个是传token,一个是传函数+next回调函数,且函数必须把token作为值传进去
下面还有更多的配置,切换视频之类的,不一一展示了,自己研究
代码实现:
创建一个djangorestframework的项目,项目名为EncryptVideo,app名为app:
url:
polyv对象,在项目根目录创建utils文件夹,该文件夹下创建polyv文件,定义一个Polyv对象
利用了设计模式里的单例模式返回一个polyv_video对象
view:request.META.get('REMOTE_ADDR') 可以获取客户端的IP地址 导入那个polyv对象
html,注意返回数据的层级,这个得根据你返回的数据来定
在这之前,我建议最好写一个解决跨域请求的中间件,也放在utils目录下:
启动项目:Python manage.py runserver localhost:8000
那个html文件利用pycharm的功能从浏览器打开:
展示结果,朋友们,如果你遇到了这些坑,可以按我的方法试试
第一个坑
(为了不浪费大家时间,我上面给的截图其实已经是我修改过并且正确的了)
发现返回的结果,sign params invild,意思就是说sign参数无效,那么再看官方文档:
sign的计算规则根本由这个concated生成的:
但是这句话,有歧义,按照ASCII升序拼接,到底是先拼接了之后再按ASCII升序还是先按ASCII升序之后再拼接,而且他这句,按照ASCKII升序 key + value + key + value ... +value 拼接,确实不知道到底怎么拼接,所以我是这样的:
当然你也可以用列表生成式,反正怎么舒服怎么来,反正拼接顺序就是先按key排序之后再key+value组合
第二个坑,这个坑我个人认为操作的问题,其实不算坑,如果你遇到跟我一样的问题,那恭喜你 嘻嘻
(上面的截图也是正确的了,不浪费大家时间)
可以显示,但是点击播放放不了,打开控制台,报错了:
(ip地址可以忽略,这是我之前测试的时候遇到的问题 )
这个问题,我跟你说,看似是同源策略的问题,其实并不是,就是我前面标注的那里:
就是因为两个url没有统一导致的,所以必须要统一,要嘛都在结束符【/】,要嘛都不带
第三个坑,错误的以为保利威方给你报同源策略错误
(上面截图也是已经是正确的了)
展示结果还是不能播放,并且连视频缩略图都没了,而且如图:
这个问题我是耗在这耗时最久的,报错的意思就是跨域请求了,浏览器同源策略的问题,但是我把本地的启动ip改成了我局域网的ip【192.168.0.8:8000】,html部分axios异步请求那里的也是【192.168.0.8:8000】,然后在django配置文件的这里,我添加了这个
中间价对response的设置前面也设置了,启动还是不行,后面突然醒悟过来,打印看token是否有拿到,确实有拿到
而且在我们这个平台,保利威,客户三者之间的关系,其实是这样的:
也就是说,这个问题就是因为第2步之后的第3步上卡住,产生了跨域请求,所以这跟我们没多大关系了,是保利威视频那边的问题。前面这句话前半句是对的(“第2步之后的第3步上卡住了”),后面的分析都是错的,但是当时的我不知道啊,按着错误的思路,我想了想,我在保利威后台设置了一个视频域名白名单:localhost:8000
有朋友回问,保利威那边默认不就是对任何域名都没有限制的吗?是啊,但是我还是设置了,设置之后,果然还是不行,我还溯源,准备从这个next参数的开始:
我还去分析了他们的那个js文件,想找找next到底是什么:
发现简直无从下手的。最后我就真的以为是保利威那边的问题域名问题,就是要设置那个域名才行,但是我这里改下,那里改下还成功了:
可以播放了,可以调播放速度啥的,注意,默认打开没有声,是因为默认音量按钮没开,自己点那个喇叭图标打开音量
那按着这个错误的思路,有朋友会说,我设置域名是127.0.0.1:8000,把项目的启动ip也启动为本地地址看看:
访问:
所以还是不能直接是ip地址。
但是!但是,根据我的经验,我还是不太放心,我又新建了一个django项目:
逻辑一样,然后启动的就是127.0.0.1:8001,然后保利威视频域名限制我也删了:
发现照样能播放:
最后经我的研究发现,还是获取token那里有问题,我把代码重新写的非常浅显明了,什么列表生成式的写法都弃了,就为了读代码顺畅(上面的代码截图给的已经是正确的了)
结果这样确实可以正常获取token,然后next函数传入token就直接播放了。
所以我错误的以为保利威那边的问题,饶了很久才发现。
以上是我个人的从分析问题,走错分析的路掉进坑了,然后得到的总结,如果你们没有遇到,一气呵成,那么你很棒,反正我是遇到了这些坑,最后不放心又测试了一次才找到根本问题的。当然以上都是我个人推断,不代表绝对正确
好的,怎么代表我们真的配置好了呢?再上传一个视频,然后播放看看,如果真的没问题,那以后就没问题了,我开始上传,并修改html上的id
浏览器打开:
相关代码:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>保利威视频测试</title>
- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
- <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>
- </head>
- <body>
- <div id="player"></div>
- <script src="//player.polyv.net/script/player.js"></script>
- <script>
- var player = polyvPlayer({
- wrap: '#player',
- width: 800,
- height: 533,
- vid: '2f57a436189b03930638e752c9a3761e_2', // 视频id,对应账号里的视频id
- playsafe: function (vid, next) {
- axios.request({
- url: "http://localhost:8000/polyv",
- method: "POST",
- data: {
- vid: vid
- }
- }).then(function (data) {
- console.log(data.data);
- next(data.data.token);
- })
- }
- });
- </script>
- </body>
- </html>
html
- from django.contrib import admin
- from django.urls import path
- from app.views import Polyv
- urlpatterns = [
- path('admin/', admin.site.urls),
- path('polyv', Polyv.as_view()), # 这里要与客户端url对应,最后有没有【/】要统一
- ]
urls
- from django.conf import settings
- import time
- import requests
- import json
- import hashlib
- class PolyvPlayer(object):
- userId = settings.POLYV_CONFIG['userId']
- secretkey = settings.POLYV_CONFIG['secretkey']
- def tomd5(self, value):
- """取md5值"""
- return hashlib.md5(value.encode()).hexdigest()
- # 获取视频数据的token
- def get_video_token(self, videoId, viewerIp, viewerId=None, viewerName='', extraParams='HTML5'):
- """
- :param videoId: 视频id
- :param viewerId: 看视频用户id
- :param viewerIp: 看视频用户ip
- :param viewerName: 看视频用户昵称
- :param extraParams: 扩展参数
- :param sign: 加密的sign
- :return: 返回点播的视频的token
- """
- ts = int(time.time() * 1000) # 时间戳
- plain = {
- "userId": self.userId,
- 'videoId': videoId,
- 'ts': ts,
- 'viewerId': viewerId,
- 'viewerIp': viewerIp,
- 'viewerName': viewerName,
- 'extraParams': extraParams
- }
- # 按照官方文档,将参数 按照ASCKII升序 key + value + key + value... + value 拼接
- plain_sorted = {}
- key_temp = sorted(plain)
- for key in key_temp:
- plain_sorted[key] = plain[key]
- print(plain_sorted)
- plain_string = ''
- for k, v in plain_sorted.items():
- plain_string += str(k) + str(v)
- print(plain_string)
- sign_data = self.secretkey + plain_string + self.secretkey
- # 取sign_data的md5的大写
- sign = self.tomd5(sign_data).upper()
- # 新的带有sign的字典
- plain.update({'sign': sign})
- print('plain', plain)
- result = requests.post(
- url='https://hls.videocc.net/service/v1/token',
- headers={"Content-type": "application/x-www-form-urlencoded"}, # 一定要带上这个请求头
- data=plain
- ).json()
- data = {} if isinstance(result, str) else result.get("data", {})
- return {"token": data}
- polyv_video = PolyvPlayer()
polyv.py
- from django.utils.deprecation import MiddlewareMixin
- class MyCorsMiddelware(MiddlewareMixin):
- def process_response(self, request, response):
- response["Access-Control-Allow-Origin"] = "*"
- if request.method == "OPTIONS":
- response["Access-Control-Allow-Headers"] = "*"
- return response
middlewares
- # --------- 保利威视频注册用户id和key--------
- POLYV_CONFIG = {
- 'userId': '您的id', # polyv 提供的服务器间的通讯验证
- 'secretkey': '您的secret' # polyv 提供的接口调用签名访问的key
- }
settings部分
- from django.shortcuts import render
- from rest_framework.views import APIView
- from rest_framework.response import Response
- from django.http import HttpResponse
- from utils.polyv import polyv_video
- import json
- class Polyv(APIView):
- def post(self, request):
- vid = request.data.get("vid")
- remote_addr = request.META.get("REMOTE_ADDR")
- user_id = 1
- user_name = "test"
- verify_data = polyv_video.get_video_token(vid, remote_addr, user_id, user_name)
- return Response(verify_data["token"])
views
播放跑马灯
以上步骤其实已经可以满足大部分用户了,但是有朋友发现了,这里还是可以用IDM直接下载啊:
所以接下来就要设置跑马灯了
1.设置视频授权跑马灯
根据我的观察,好像默认就开启了跑马灯的
2.设置一个授权地址
这个地址可以是本地的
3.创建一个html文件,名字随意,存入如下代码:
这里的域名部分,官方建议这样设置,其实在后面我测试的时候,发现按照下面这个设置,兼容性不好,谷歌可以播放,IE浏览器放不了
4.url部分添加一个crossdomain.xml
指向test_bolyv视图函数
视图函数:
5.视频接口/polyv添加get方法
这个get方法是保利威后台自动调用的,不是我们这边服务端要用的,也不是客户端要用的
user_name部分就是要显示的跑马灯数据
6.在定义的polyv类里添加方法:
get_play_key和get_resp
get_play_key是获取sign的,注意这里的设置sign和上面获取加密视频的sign不太一样
get_resp是做跑马灯授权的:
7.引入新的js
这里他给的例子是用的不加密的方式
我们要播放加密视频,当然还是得使用playsafe参数播放,在templates目录下新建一个tests.html,代码如下,我标注出来的就是添加的参数,同样的,注意返回数据的层级
启动项目:
通过pycharm虚拟一个客户端出来,点击那些浏览器图标打开,我的电脑是windows,所以试了谷歌,火狐和IE
谷歌:谷歌默认打开是没有声音的,这是谷歌浏览器的策略问题,不是大问题,正常播放,且正常显示跑马灯
IE:
再次强调,如果在那个xml文件里,你如果按官方的建议设置成这样:
然后你打开IE是播放不了的:
所以别按它建议的设置,就用默认的
火狐:
问题来了,就是这个问题,我折腾老久了,这个根本原因还是跨域请求问题,因为火狐浏览器默认安全性比较高,所以谷歌和IE可以,就是火狐不行,我查阅了很多,比如关闭火狐的跨域请求的,设置跨域请求的,设置了很多,还是没用,最后使用了第三方库django-cors-headers解决了,相关介绍安装文章: 前后端分离djangorestframework——解决跨域请求 第5个方法
根据操作,重启项目,火狐立马播放:
然后现在你看我用IDM点下载:
我随便选了一个:
提示:
最后再来个辅助测试,换个视频id播放看看,换回这个视频
火狐,谷歌,IE:
确实没有问题了,这样的操作是不是很6啊,我反正感觉很6
详细的参数配置步骤就没展开了,具体看官方文档吧:传送门
相关代码:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>Title</title>
- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
- <script src='https://player.polyv.net/script/polyvplayer.min.js'></script>
- <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>
- </head>
- <body>
- <div id="player"></div>
- <script>
- var player = polyvObject('#player').videoPlayer({
- wrap: '#player',
- width: 800,
- height: 533,
- forceH5: true,
- vid: '2f57a43618b400b4f9c84fcea9b103a8_2',
- code: 'myRandomCodeValue',
- playsafe: function (vid, next) { // 向后端发送请求获取加密的token
- console.log(vid);
- axios.request({
- url: "http://localhost:8000/polyv",
- method: "POST",
- data: {
- vid: vid
- }
- }).then(function (data) {
- console.log(data);
- next(data.data.token)
- })
- }
- });
- </script>
- </body>
- </html>
前端html
- <cross-domain-policy>
- <allow-access-from domain="*.polyv.net"/>
- </cross-domain-policy>
bolyv_test.html
- from django.conf import settings
- import time
- import requests
- import json
- import hashlib
- class PolyvPlayer(object):
- userId = settings.POLYV_CONFIG['userId']
- secretkey = settings.POLYV_CONFIG['secretkey']
- def tomd5(self, value):
- """取md5值"""
- return hashlib.md5(value.encode()).hexdigest()
- # 获取视频数据的token
- def get_video_token(self, videoId, viewerIp, viewerId=None, viewerName='', extraParams='HTML5'):
- """
- :param videoId: 视频id
- :param viewerId: 看视频用户id
- :param viewerIp: 看视频用户ip
- :param viewerName: 看视频用户昵称
- :param extraParams: 扩展参数
- :param sign: 加密的sign
- :return: 返回点播的视频的token
- """
- ts = int(time.time() * 1000) # 时间戳
- plain = {
- "userId": self.userId,
- 'videoId': videoId,
- 'ts': ts,
- 'viewerId': viewerId,
- 'viewerIp': viewerIp,
- 'viewerName': viewerName,
- 'extraParams': extraParams
- }
- # 按照官方文档,将参数 按照ASCKII升序 key + value + key + value... + value 拼接
- plain_sorted = {}
- key_temp = sorted(plain)
- for key in key_temp:
- plain_sorted[key] = plain[key]
- print(plain_sorted)
- plain_string = ''
- for k, v in plain_sorted.items():
- plain_string += str(k) + str(v)
- print(plain_string)
- sign_data = self.secretkey + plain_string + self.secretkey
- # 取sign_data的md5的大写
- sign = self.tomd5(sign_data).upper()
- # 新的带有sign的字典
- plain.update({'sign': sign})
- print('plain', plain)
- result = requests.post(
- url='https://hls.videocc.net/service/v1/token',
- headers={"Content-type": "application/x-www-form-urlencoded"}, # 一定要带上这个请求头
- data=plain
- ).json()
- data = {} if isinstance(result, str) else result.get("data", {})
- return {"token": data}
- def get_play_key(self, vid, username, code, status, ts):
- """
- :param vid: 视频 vid
- :param username: 响应跑马灯展示
- :param code: 自定义参数
- :param status: 是否可播放, 1、可播放 2、禁播
- :param ts: 时间戳
- :return: 返回跑马灯视频的key
- """
- return self.tomd5("vid={}&secretkey={}&username={}&code={}&status={}&t={}".format(
- vid, self.secretkey, username, code, status, ts)).lower()
- @staticmethod
- def get_resp(status, username, sign, msg="授权暂未通过"):
- res_str = {
- "status": status,
- "username": username,
- "sign": sign,
- "msg": msg,
- ",
- "fontColor": "0xFF0000",
- ",
- "filter": "on",
- ",
- "alpha": "0.7",
- ",
- "filterColor": "0x3914AF",
- ",
- ",
- ",
- ",
- ",
- ",
- "show": "on"
- }
- return res_str
- polyv_video = PolyvPlayer()
polyv.py - 自定义的polyv类
- from django.shortcuts import render
- from rest_framework.views import APIView
- from rest_framework.response import Response
- from django.http import HttpResponse
- from utils.polyv import polyv_video
- import json
- class Polyv(APIView):
- def post(self, request):
- vid = request.data.get("vid")
- remote_addr = request.META.get("REMOTE_ADDR")
- user_id = 1
- user_name = "test"
- verify_data = polyv_video.get_video_token(vid, remote_addr, user_id, user_name)
- return Response(verify_data["token"])
- def get(self, request, *args, **kwargs):
- vid = request.query_params.get("vid", "")
- code = request.query_params.get("code", "")
- t = request.query_params.get("t", "")
- callback = request.query_params.get("callback", "")
- user_name = "test|1234565"
- status = 1
- # username, code, status, t
- sign = polyv_video.get_play_key(vid, user_name, code, status, t)
- print(sign)
- res_str = polyv_video.get_resp(int(status), user_name, sign)
- res_str = json.dumps(res_str, ensure_ascii=False)
- if callback != "":
- ret = callback + "(" + res_str + ")"
- else:
- ret = res_str
- print(ret)
- return HttpResponse(ret)
- # 解决跨域
- def test_bolyv(request):
- return render(request, "bolyv_test.html")
views
- from django.contrib import admin
- from django.urls import path, re_path
- from app.views import test_bolyv
- from app.views import Polyv
- urlpatterns = [
- path('admin/', admin.site.urls),
- path('polyv', Polyv.as_view()), # 这里要与客户端url对应,最后有没有【/】要统一
- re_path(r'^crossdomain.xml', test_bolyv)
- ]
urls
- INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'app.apps.AppConfig',
- 'corsheaders',
- ]
- MIDDLEWARE = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'corsheaders.middleware.CorsMiddleware',
- 'django.middleware.common.CommonMiddleware', # 注意顺序
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
- # 'utils.middlewares.MyCorsMiddelware' # 这是我们自己定义的那个中间件
- ]
- # --------- 保利威视频注册用户id和key--------
- POLYV_CONFIG = {
- 'userId': '您的id', # polyv 提供的服务器间的通讯验证
- 'secretkey': '您的secret' # polyv 提供的接口调用签名访问的key
- }
- # 跨域增加忽略
- CORS_ALLOW_CREDENTIALS = True
- CORS_ORIGIN_ALLOW_ALL = True
- CORS_ORIGIN_WHITELIST = (
- '*'
- )
- CORS_ALLOW_METHODS = (
- 'DELETE',
- 'GET',
- 'OPTIONS',
- 'PATCH',
- 'POST',
- 'PUT',
- 'VIEW',
- )
- CORS_ALLOW_HEADERS = (
- 'XMLHttpRequest',
- 'X_FILENAME',
- 'accept-encoding',
- 'authorization',
- 'content-type',
- 'dnt',
- 'origin',
- 'user-agent',
- 'x-csrftoken',
- 'x-requested-with',
- )
settings.py - 部分配置
总结
- 经过这前后几个接入第三方平台的案例,发现其实都不算难,但是都有一些坑,必须要自己去实践去研究了才能玩得会
- 火狐浏览器的安全级别默认很高,需要借助第三方库来配置
- 跑马灯视频设置get请求的url必须和主逻辑API接口是同一个
- 遇到问题还是多研究,多理解,多分析
前后端分离djangorestframework—— 在线视频平台接入第三方加密防盗录视频的更多相关文章
- 前后端分离djangorestframework—— 接入第三方的验证码平台
关于验证码部分,在我这篇文章里说的挺详细的了:Python高级应用(3)—— 为你的项目添加验证码 这里还是再给一个前后端分离的实例,因为极验官网给的是用session作为验证的,而我们做前后端分离的 ...
- 前后端分离djangorestframework——分页组件
Pagination 为什么要分页也不用多说了,大家都懂,DRF也自带了分页组件 这次用 前后端分离djangorestframework——序列化与反序列化数据 文章里用到的数据,数据库用的my ...
- 前后端分离djangorestframework——路由组件
在文章前后端分离djangorestframework——视图组件 中,见识了DRF的视图组件强大,其实里面那个url也是可以自动生成的,就是这么屌 DefaultRouter urls文件作如下调整 ...
- 前后端分离djangorestframework——视图组件
CBV与FBV CBV之前说过就是在view.py里写视图类,在序列化时用过,FBV就是常用的视图函数,两者的功能都可以实现功能,但是在restful规范方面的话,CBV更方便,FBV还要用reque ...
- 前后端分离djangorestframework——序列化与反序列化数据
我们写好后端的代码,要把数据交给前端的展示的,这个数据以什么类型给前端呢?学到这里,我们已经知道这个数据最好是json字符串才行,因为网络间的传输,只认字符串或者二进制,字符串就是我们的数据,二进制就 ...
- 前后端分离djangorestframework——认证组件
authentication 认证是干嘛的已经不需要多说.而前后端未分离的认证基本是用cookie或者session,前后端分离的一般用token 全局认证 先创建一个django项目,项目名为drf ...
- 前后端分离djangorestframework——restful规范
restful现在非常流行,所以很有必要提一下 web服务交互 在浏览器中能看到的每个网站,都是一个web服务.那么我们在提供每个web服务的时候,都需要前后端交互,前后端交互就一定有一些实现方案,我 ...
- 前后端分离djangorestframework—— 接入支付宝支付平台
支付宝 简介 支付宝是什么不用多说了,本次教程适合初学者 前提准备 话不多说,干就完了 1.注册开发者账号,设置公钥私钥 首先进入支付宝开发者平台:传送门 ,有账号直接登录,没账号用你平时用来付款收钱 ...
- 前后端分离djangorestframework—— 接入微信模板消息推送
微信 什么是微信也不多说,跟前面的支付宝一样的 微信支付 微信支付也有个沙箱环境,沙箱环境官方文档 由文档中那句很显眼的话所得,即使是测试环境也需要真实的商户号,所以这个就没法想支付宝那样用沙箱账号来 ...
随机推荐
- [Swift]LeetCode942. 增减字符串匹配 | DI String Match
Given a string S that only contains "I" (increase) or "D" (decrease), let N = S. ...
- Java数据结构和算法 - 什么是2-3-4树
Q1: 什么是2-3-4树? A1: 在介绍2-3-4树之前,我们先说明二叉树和多叉树的概念. 二叉树:每个节点有一个数据项,最多有两个子节点. 多叉树:(multiway tree)允许每个节点有更 ...
- CentOS随笔——Service与防火墙关闭
Service后台服务管理 基本语法 service 服务名 start 开启服务 service 服务名 stop 关闭服务 service 服务名 restart 重启服务 service 服务名 ...
- javascript之reduce()方法的使用
以前看到reduce方法,总是看得我头皮发麻,今天无意间又遇到他了,于是学习了下,接触之后,觉得这个方法还挺好用的,在很多地方都可以派上用场,比如,数组中元素求和.数组去重.求数组中的最大值或最小值等 ...
- 记录eclipse安装SpringBoot插件及搭建SpringBoot项目
刚学习了下SpringBoot 插件安装 创建项目在此记录下 在spring官网上下载相关的插件,然后导入到eclipse中,以下是下载步骤: 1.首先查看自己eclipse版本号 help--> ...
- 【转】Kali更新源
1.切换到root用户(如果已经是root用户就直接看第二步) dnt@Kali:~$ su 2.用vim打开sources.list,手动添加下面的更新源 root@Kali:~# vim /etc ...
- IView组件化之部署及按钮学习
IView是什么? iView 是一套基于 Vue.js 的开源 UI 组件库,主要服务于 PC 界面的中后台产品. Npm安装IView npm install iview 在main.js中配置I ...
- Elasticsearch Search API
当执行一个搜索时,它将这个搜索请求广播给所有的索引分片.可以通过提供路由参数来控制要搜索哪些分片.例如,当检索tweets这个索引时,路由参数可以设置为用户名: curl -X POST " ...
- C语言用regcomp、regexec、regfree和regerror函数实现正则表达式校验
前言 首先,祝大家国庆假期玩的嗨皮!可能有的人已经在回家的路上了,是不是都看不到我的真挚祝福了? C语言对于一些东西的封装比较少,比如正则表达式,但速度快一直使它立于不败之地,今天就要介绍如何用C封装 ...
- 一篇文章带你学会Linux三剑客之一:awk
awk是一种用于处理文本.模式匹配的编程语言.与sed和grep,俗称Linux下的三剑客.学会 awk 等于你在 Linux 命令行里,又多了一种处理文本的选择.这篇文章重点教你如何使用,看完这篇文 ...