Flask基于Token二次认证实现文件上传下载

1、功能说明:

脚本基于Flask实现简单文件上传下载,登录接口二次鉴权且,内部接口基于Token认证

2、目录结构:

flask-upload/              #项目目录

    conf/                      #配置文件目录

        appconf.py        #配置文件

        __init__.py

    lib/                        #库文件存放路径

    templates/            #模板文件存放路径

        upload.html      #前端页面upload上传页面

    upload/                 #上传文件保存路径

    main.py                #主程序

    README.md       #说明文档

    start.sh                #启动文件

3、代码展示:

# _*_ coding:utf-8 _*_
''' 用于文件上传下载 '''

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import os
basedir = os.path.dirname(os.path.abspath(__file__))
libdirs = os.path.join(basedir,'lib')
confdir = os.path.join(basedir,'conf')
logpath = os.path.join(basedir,'logs')
inifile = os.path.join(confdir,'__init__.py')
appfile = os.path.join(confdir,'appconf.py')
sys.path.insert(0,libdirs)

import time
import hmac
import base64
import random
import string
import hashlib
from werkzeug.utils import secure_filename
from flask import Flask,render_template,jsonify,request,send_from_directory,redirect,url_for,make_response

app=Flask(__name__)
app.debug=True

#生成随机8位字符串
SALT = ''.join(random.sample(string.ascii_letters + string.digits, 8))

#生成默认Conf配置文件
if os.path.isdir(confdir) is False:
    os.makedirs(confdir)
with open(inifile,'w') as f:
    f.write('from appconf import init\nConf = init()\n')
if os.path.exists(appfile) is False:
    with open(appfile,'w') as f:
        f.write('''# _*_ coding:utf-8 _*_
def init():
    return {
        'global':{
            'default_key':'000000',
            'upload_dir':'upload', #默认上传路径
            'secret_key':'key%s', #Token加盐salt值
            'expire_time':3600, #Token过期时间
            'limit_size': 100 * 1024 * 1024,
            'allowed_extensions':['sh','py','gz','tar.gz','zip','tar'] #支持上传的文件类型
        },
        'ulogin':{
            'admin': '%s' #默认登录账户密码
        }
    }
''' % (int(time.time()),SALT))

from conf import Conf

URINFO=Conf['ulogin']
SECKEY=Conf['global']['secret_key']
EXPIRE=Conf['global']['expire_time']
UPLOAD_FOLDER=Conf['global']['upload_dir']
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH']=Conf['global']['limit_size']
ALLOWED_EXTENSIONS = set(Conf['global']['allowed_extensions'])

# 用于判断文件后缀
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.',1)[1] in ALLOWED_EXTENSIONS

#登录接口(需要二次登录通过Seckey登录)
@app.route('/login',methods=['GET','POST'])
def do_login():
    username = password = None
    try:
        if request.method == 'GET':
            username=request.args.get('username',None)
            password=request.args.get('password',None)
        if request.method == 'POST':
            username=request.json.get('username',None)
            password=request.json.get('password',None)
        if username is None:
            return jsonify({"code":1,"message":"The username is empty"})
        if password is None:
            return jsonify({"code":1,"message":"The password is empty"})
        if username not in URINFO:
            return jsonify({"code":1,"message":"The username is not exists"})
        if URINFO.get(username) not in password:
            return jsonify({"code": 1,"message": "The password is error"})
        tmp_code=Conf['global']['default_key']
        if request.cookies.has_key('Seckey'):
            tmp_code=request.cookies.get('Seckey',tmp_code)
        if password != URINFO.get(username)+ tmp_code:
            tmp_code = ''.join(random.sample(string.ascii_letters + string.digits,16))
            response = make_response(jsonify({"code": 0,"seckey":tmp_code}))
            response.set_cookie('Seckey', tmp_code)
            response.set_cookie('Username', username)
            return response
        Token=encrypt_token(key=username,expire=EXPIRE)
        response=make_response(jsonify({"code":0,"message":"Login successful"}))
        response.set_cookie('Username',username)
        response.set_cookie('Token',Token)
        return response
    except Exception,e:
        response = make_response(jsonify({"code":1,"message":"Login failed"}))
        response.set_cookie('Username',username)
        return response

#注销接口
@app.route('/logout',methods=['GET'])
def do_logout():
    Token=request.cookies.get('Token',None)
    Username=request.cookies.get('Username',None)
    if decrypt_token(token=Token,key=Username) is False:
        return jsonify({"code":1,"message":"User logout fails,Token authentication fails"})
    try:
        response=make_response(jsonify({"code":0,"message":"User logout successful"}))
        response.delete_cookie('Username',Username)
        response.delete_cookie('Token',Token)
        return response
    except Exception:
        return jsonify({"code": 1, "message": "User logout failed"})

#生成Token
def encrypt_token(key=SECKEY,expire=EXPIRE):
    time_str=str(time.time() + expire).encode('utf-8')
    hmac_str=hmac.new(key.encode('utf-8'),time_str,hashlib.md5().update(SECKEY)).hexdigest()
    hash_str=hmac_str.encode('utf-8') + ':' + time_str
    token_str=base64.urlsafe_b64encode(hash_str).encode('utf-8')
    return token_str

#验证Token
def decrypt_token(token,key=SECKEY):
    try:
        token_str=base64.urlsafe_b64decode(token.encode('utf-8'))
        hash_str=token_str.split(':')
        if len(hash_str) != 2:
            return False
        hmac_str=hash_str[0]
        time_str=hash_str[1]
        if time.time() > float(time_str):
            return False
        old_hmac_str=hmac.new(key.encode('utf-8'),time_str.encode('utf-8'),hashlib.md5().update(SECKEY)).hexdigest()
        if hmac_str != old_hmac_str:
            return False
        return True
    except Exception:
        return False

#上传页面
@app.route('/upload')
def upload_render():
    Token=request.cookies.get('Token',None)
    Username=request.cookies.get('Username',None)
    if decrypt_token(token=Token,key=Username) is False:
        return jsonify({"code":1,"message":"Token Authentication failed"})
    return render_template('upload.html')

#下载接口
@app.route('/download',methods=['GET'],strict_slashes=False)
def download():
    Token=request.cookies.get('Token',None)
    Username = request.cookies.get('Username', None)
    if decrypt_token(token=Token,key=Username) is False:
        return jsonify({"code":1,"message":"Token Authentication failed"})
    filename=request.args.get('file',None)
    if filename is None:
        return jsonify({"code":1,"message":"File is None"})
    if os.path.isfile(os.path.join('upload', filename)):
        return send_from_directory('upload',filename,as_attachment=True)
    return jsonify({"code": 1, "message": "File does not exist"})
 
#上传接口
@app.route('/api/upload',methods=['POST','GET'],strict_slashes=False)
def api_upload():
    Token=request.cookies.get('Token',None)
    Username = request.cookies.get('Username', None)
    if decrypt_token(token=Token,key=Username) is False:
        return jsonify({"code":1,"message":"Token Authentication failed"})
    if request.method == 'GET':
        return render_template('upload.html')
    file_dir=os.path.join(basedir,app.config['UPLOAD_FOLDER'])
    if not os.path.exists(file_dir):
        os.makedirs(file_dir)
    f=request.files['myfile']  # 从表单的file字段获取文件,myfile为该表单的name值
    try:
        if f.filename == '':
            return render_template('upload.html')
        if f and allowed_file(f.filename):  # 判断是否是允许上传的文件类型
            fname=secure_filename(f.filename)
            fname=str(fname).encode('utf-8')
            if os.path.exists(os.path.join(file_dir,fname)) is False:
                f.save(os.path.join(file_dir,fname))
            else:
                eot = fname.split('.',1)[0].encode('utf-8')
                ext = fname.split('.',1)[1]  # 获取文件后缀
                unix_time = int(time.time())
                new_filename=eot+'_'+str(unix_time)+'.'+ext  # 修改了上传的文件名
                f.save(os.path.join(file_dir,new_filename))  #保存文件到upload目录
            time.sleep(3)
            return redirect(url_for('upload_render'))
        else:
            return jsonify({"code":1,"message":"File type not allowed for upload"})
    except Exception:
        return jsonify({"code": 1, "message": "File type not allowed for upload"})
 
if __name__ == '__main__':
    app.run(host='0.0.0.0',port=5678)

接口验证:

登录接口:http://ip:5678/login?username=用户&password=密码,用户密码请看conf/appconf.py配置文件ulogin配置块

验证上传接口:

http://ip地址:5678/upload

由于未二次登录所有上传接口Token验证失败,执行二次登录如下:

注意二次登录password=密码+seckey

登录成功如下:

再次刷新上传接口已经正常显示界面

 

执行文件上传

验证文件上传

文件下载:

http://ip地址:5678/download?file=文件名称

注销登录:

http://ip地址:5678/logout

至此文件上传下载验证完成,下载地址如下:

链接:https://pan.baidu.com/s/1q1CA-IRxqQXwUEXKD67Yzg 
提取码:1avq 
 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请叫俺大神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值