[ Nginx ] 微服務權限管理

最近被問到,微服務系統要怎麼管理權限。上網查了一下,發現這方面的文章好像不多,加上好久沒更新了,想說就來分享一下。
我傾向權限通通交給 server來管理,這樣不管是用什麼樣的方式來驗證用戶,都不影響其他微服務運作。

因為要依賴到 Nginx,所以就直接使用 Docker了,先建立一個 docker-conpose.yaml文件。
version: '3'
services:
  web:
    build:
      context: ./web
      dockerfile: Dockerfile
    command: gunicorn -b 0.0.0.0:8000 src.wsgi
    ports:
      - "8000:8000"
    volumes:
      - ./web/src:/web/src
      - ./volumes/media-root:/web/media-root
  nginx:
    image: nginx
    ports:
      - "80:80"
    volumes:
      - ./volumes/media-root:/nginx/media-root
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
這個 yaml檔目的是運行 Nginx,其中主要是將設定檔放入 container中。

接著建立一個資料夾,並在裡面建立一個文件 default.conf。
server {
  listen 80 default_server;

  location /protectedMedia {
    internal;
    alias /nginx/media-root;
  }

  location / {
    proxy_pass http://web:8000;
  }
}
在 server的區塊中建立兩個路徑,"/""/protectedMedia","/"將會作為 Django的 proxy,"/protectedMedia"則會專門處理路徑/nginx/media-root中的靜態文件,其中 internal的設定是指這個路徑僅接受內部訪問,所以是沒辦法直接從瀏覽器訪問的。
不打算使用 Docker的朋友可以直接安裝 nginx(怎麼安裝網上應該很好查),然後直接修改 /etc/nginx/conf.d/default.conf(位置可能會有所差異),但要將"http://web:8000"修改成"http://localhost:8000。

Django建立的流程不是本文重點,所以就直接切入重點。先建立一個資料夾 web,並在裡面建立一個 Django專案(這個 demo將專案命名為 src)。接著在專案中建立資料夾 media,並在裡面建立 __init__.py和 views.py。
首先修改 src/urls.py。
# src/urls.py

from django.contrib import admin
from django.urls import path, re_path

from media.views import Media

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'media/(?P<filepath>.+)', Media.as_view(), name='media'),
]
這裡需要使用到 re_path,允許開發者使用 regular expression來辨識前端請求的 url。
其中 r'media/(?P<filepath>.+)'是獲取所有在/media/後的字串,並放入 filePath的變數中。
# media/views.py

from django.views import View
from django.http import HttpResponse
from django.utils.http import urlquote

class Media(View):
    def get(self, request, filePath):
        filePath = urlquote(filePath)
        response = HttpResponse()
        url = '/protectedMedia/{}'.format(filePath)
        response['X-Accel-Redirect'] = url
        response['Content-Type'] = ''
        return response
獲取 filePath後就可以建立訪問 Nginx中的 /protectedMedia的完整路徑。
urlquote()的用途是當 filePath中有非英文符號時,會對該字串進行轉譯。
接著將路徑放入 header裡的 X-Accel-Redirect
最後在 Content-Type中放一個空字串。(建立空的 Content-Type目的是讓 Nginx自己判斷 response的 Content-Type的值,若不設定可能會誤以為用戶是要下載等狀況)

到此,訪問 internal的設定就已經完成了。 最後資料夾結構如下。
當中我已經在/nginx/media-root的路徑中放了名為"job_hunt.jpg"的圖片。
接著就直接下指令"docker-conpose up --build",就可以透過一般訪問 media的方式訪問 media中的靜態文件。
不是使用 Docker的朋友就直接啟動 Django並重啟 Nginx即可。
最後直接在瀏覽器上訪問 http://localhost/media/job_hunt.jpg。
這次文章簡化了很多步驟,主要是分享如何透過 server訪問 Nginx中 internal的連結,因此沒有真的設定權限,會觀看本文的朋友,應該已經有基本的能力在 server端對連結設定權限了。
雖然本例並沒有提供其他的微服務,但可以用同樣的方式,將處理 media的區塊修改成其他微服務的 proxy。
另外,不一定要從 url來告訴 server檔案路徑,可以透過任何方式,比如透過 primary key取得檔案路徑等,最終只需要設定 response['X-Accel-Redirect']時給予正確的路徑即可。這樣就不一定要寫麻煩的 regular expression了。
若有什麼不清楚的地方,歡迎參考我的Bitbucket

觀察了一下,台灣 Django的職缺真的好少,幾年下來都沒什麼變化,跟 Ruby on Rails差好多。(淚

留言