Nginx配置
Ngnix,一個高性能的web服務器,毫無疑問它是當下的寵兒。卓越的性能,靈活可擴展,在服務器領域里攻城拔寨,征戰天下。
靜態文件對于大多數website是不可或缺的一部分。使用Nginx來處理靜態文件也是常見的方式。然而,一些靜態文件,我們并不像任何情況下都公開給任何用戶。例如一些提供給用戶下載的文件,一些用戶上傳的涉及用戶隱私的圖片等。我們我希望用戶登錄的情況下可以訪問,未登錄的用戶則不可見。
粗略的處理,在后端程序可以做過濾,渲染頁面的時候,在視圖邏輯里面驗證用戶登錄,然后返回對應的頁面。例如下面的flask代碼(偽代碼)
@app.router('/user/idcard'):def user_idcard_page(): if user is login: return '<img src="http://files.VeVB.COm/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_requireddef 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 structproject 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 wrapsfrom flask import Flask, render_template, redirect, url_for, session, send_fileapp = 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_requireddef user(): return render_template('upload.html')# 用戶上傳的文件視圖處理,在此處返回請求給nginx@app.route('/upload/<filename>')@login_requireddef 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
新聞熱點
疑難解答
圖片精選