Python的Flask框架及Nginx实现静态文件访问限制功能


Posted in Python onJune 27, 2016

Nginx配置

Ngnix,一个高性能的web服务器,毫无疑问它是当下的宠儿。卓越的性能,灵活可扩展,在服务器领域里攻城拔寨,征战天下。

静态文件对于大多数website是不可或缺的一部分。使用Nginx来处理静态文件也是常见的方式。然而,一些静态文件,我们并不像任何情况下都公开给任何用户。例如一些提供给用户下载的文件,一些用户上传的涉及用户隐私的图片等。我们我希望用户登录的情况下可以访问,未登录的用户则不可见。

粗略的处理,在后端程序可以做过滤,渲染页面的时候,在视图逻辑里面验证用户登录,然后返回对应的页面。例如下面的flask代码(伪代码)

@app.router('/user/idcard'):
def user_idcard_page():
 if user is login:
  return '<img src="/upload/user/xxx.png'>"
 else:
  reutrn '<p>Pemission Denied<p>', 403

可是这样的处理,还有一个问题,静态文件是交给 nginx 处理的,如果hacker找到了文件的绝对地址,直接访问 http://www.example.com/upload/user/xxx.png也是可以的。恰巧这些文件又涉及用户隐私,比如用户上传的身份证照片。那么码农可不希望第二天媒体报道,知名网站XXX存在漏洞,Hacker获取了用户身份证等信息。

为了做这样的限制,可以借助 Nginx 的一个小功能----XSendfile。 其原理也比较简单,大概就是使用了请求重定向。

我们知道,如果用Nginx做服务器前端的反向代理,一个请求进来,nginx先补捉到,然后再根据规则转发给后端的程序处理,或者直接处理返回。前者处理一些动态逻辑,后者多是处理静态文件。因此上面那个例子中,直接访问静态文件的绝对地址,Nginx就直接返回了,并没有调用后端的 user_idcard_page做逻辑限制。

为了解决这个问题,nginx提供的 XSendfile功能,简而言之就是用 internal 指令。该指令表示只接受内部的请求,即后端转发过来的请求。后端的视图逻辑中,需要明确的写入X-Accel-Redirect这个headers信息。

伪代码如下:

location /upload/(.*) {
  alias /vagrant/;
  internal;
}

@app.router('upload/<filename>')
@login_required
def upload_file(filename):
 response = make_response()
 response['Content-Type'] = 'application/png'
 response['X-Accel-Redirect'] = '/vagrant/upload/%s' % filename
 return response

经过这样的处理,就能将静态资源进行重定向。这样的用法还是比较常见的,很多下载服务器可以通过这样的手段针对用户的权限做下载处理。

Flask

Flask是我喜欢的web框架,Flask甚至实现了一个 sendfile的方法,比上面的做法还简单。我用vagrant作了一个虚拟机,用Flask实现了上面的需求,具体代码如下:

项目结构

project struct

project
 app.py
 templates
 static
 0.jpeg
 upload
 0.jpeg

nginx的配置 nginx conf

web.conf

server {
  listen 80 default_server;

  # server_name localhost;
  server_name 192.168.33.10;
  location / {
    proxy_pass http://127.0.0.1:8888;
    proxy_redirect off;
    proxy_set_header Host $host:8888;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
  # 正常的静态文件
  location /static/(.*) {
    root /vagrant/;

  }
  # 用户上传的文件,需要做权限限制
  location /upload/(.*) {
    alias /vagrant/;
    internal;    # 只接受内部请求的指令
  }
}

Flask 代码

app.py

from functools import wraps
from flask import Flask, render_template, redirect, url_for, session, send_file

app = Flask(__name__)

app.config['SECRET_KEY'] = 'you never guess'

def login_required(f):
 @wraps(f)
 def decorated_function(*args, **kwargs):
  if not session.get('login'):
   return redirect(url_for('login', next=request.url))
  return f(*args, **kwargs)
 return decorated_function

@app.route('/')
def index():
 return 'index'


@app.route('/user')
@login_required
def user():

 return render_template('upload.html')

# 用户上传的文件视图处理,在此处返回请求给nginx
@app.route('/upload/<filename>')
@login_required
def upload(filename):

 return send_file('upload/{}'.format(filename))


@app.route('/login')
def login():
 session['login'] = True
 return 'log in'

@app.route('/logout')
def logout():
 session['login'] = False
 return 'log out'


if __name__ == '__main__':
 app.run(debug=True)

简单部署

gunicorn -w4 -b0.0.0.0:8888 app:app --access-logfile access.log --error-logfile error.log
Python 相关文章推荐
python冒泡排序算法的实现代码
Nov 21 Python
python使用SMTP发送qq或sina邮件
Oct 21 Python
Python 将Matrix、Dict保存到文件的方法
Oct 30 Python
详解安装mitmproxy以及遇到的坑和简单用法
Jan 21 Python
Django 权限认证(根据不同的用户,设置不同的显示和访问权限)
Jul 24 Python
PyQt Qt Designer工具的布局管理详解
Aug 07 Python
解决Atom安装Hydrogen无法运行python3的问题
Aug 28 Python
30秒学会30个超实用Python代码片段【收藏版】
Oct 15 Python
python  ceiling divide 除法向上取整(或小数向上取整)的实例
Dec 27 Python
浅谈Python3实现两个矩形的交并比(IoU)
Jan 18 Python
python 数据库查询返回list或tuple实例
May 15 Python
Python re.sub 反向引用的实现
Jul 07 Python
总结网络IO模型与select模型的Python实例讲解
Jun 27 #Python
结合Python的SimpleHTTPServer源码来解析socket通信
Jun 27 #Python
Python的Tornado框架的异步任务与AsyncHTTPClient
Jun 27 #Python
深入解析Python中的descriptor描述器的作用及用法
Jun 27 #Python
Python中的字符串查找操作方法总结
Jun 27 #Python
解析Python中的__getitem__专有方法
Jun 27 #Python
详解Python中的__getitem__方法与slice对象的切片操作
Jun 27 #Python
You might like
PHP4在Windows2000下的安装
2006/10/09 PHP
PHP递归删除目录几个代码实例
2014/04/21 PHP
PHP实现自动发送邮件功能代码(qq 邮箱)
2017/08/18 PHP
下载网站打开页面后间隔多少时间才显示下载链接地址的代码
2010/04/25 Javascript
node在两个div之间移动,用ztree实现
2013/03/06 Javascript
javascript实现的DES加密示例
2013/10/30 Javascript
JavaScript中的eval()函数使用介绍
2014/12/31 Javascript
JavaScript和HTML DOM的区别与联系及Javascript和DOM的关系
2015/11/15 Javascript
jQuery增加与删除table列的方法
2016/03/01 Javascript
JavaScript模拟数组合并concat
2016/03/06 Javascript
React组件生命周期详解
2017/07/03 Javascript
Mongoose中document与object的区别示例详解
2017/09/18 Javascript
js 实现ajax发送步骤过程详解
2019/07/25 Javascript
TypeScript 引用资源文件后提示找不到的异常处理技巧
2020/07/15 Javascript
详解三种方式在React中解决绑定this的作用域问题并传参
2020/08/18 Javascript
Vue项目中使用mock.js的完整步骤
2021/01/12 Vue.js
[01:04:30]Fnatic vs Mineski 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
[01:02:09]Liquid vs TNC 2019国际邀请赛淘汰赛 胜者组 BO3 第二场 8.21
2020/07/19 DOTA
python使用urllib2实现发送带cookie的请求
2015/04/28 Python
解析Python编程中的包结构
2015/10/25 Python
Python黑魔法Descriptor描述符的实例解析
2016/06/02 Python
python使用pil进行图像处理(等比例压缩、裁剪)实例代码
2017/12/11 Python
pandas的唯一值、值计数以及成员资格的示例
2018/07/25 Python
python实现括号匹配的思路详解
2018/08/23 Python
Python字典中的键映射多个值的方法(列表或者集合)
2018/10/17 Python
python 字符串只保留汉字的方法
2018/11/16 Python
基于python分析你的上网行为 看看你平时上网都在干嘛
2019/08/13 Python
Python3.7+tkinter实现查询界面功能
2019/12/24 Python
html5唤起app的方法
2017/11/30 HTML / CSS
巴西男士个人护理产品商店:SHOP4MEN
2017/08/07 全球购物
退休感言
2014/01/28 职场文书
取保候审保证书
2014/04/30 职场文书
俞敏洪一分钟演讲稿
2014/08/26 职场文书
2015国庆节感想
2015/08/04 职场文书
运动会广播稿100字
2015/08/19 职场文书
2016年十一促销广告语
2016/01/28 职场文书