Django Rest Framework 및 SPA-세션 인증 (Docker 및 Nginx 사용)

이 기사에서는 Django, Django Rest Framework, React, 그리고 Docker와 Nginx에 대해 잘 알고 있다고 가정합니다. 그러나 나는 Docker와 Nginx를 처음 접한다고 말해야합니다. 또한 Python, Pip, Node 및 Docker가 설정되어 있다고 가정합니다. Pipenv를 사용할 것이지만 Python 종속성을 전역 적으로 설치하려면 중지 할 수 없습니다. 내 목표는 최근에 단일 페이지 애플리케이션 (SPA) 및 Django Rest Framework와 함께 Django 세션 인증을 사용하기 위해 작업 한 개발 설정을 보여주는 것입니다.

최근에 Django Rest Framework (DRF) 백엔드를 사용하는 동안 React 앱에서 사용자를 인증하려고했습니다. 가장 간단한 옵션은 JWT 토큰 인증을 사용하는 것 같았지만 빠른 Google 검색을 통해 localstorage에 JWT를 유지하는 것이 안전한지 여부에 대한 답이없는 논쟁이있는 것처럼 보입니다 (이 진술은 지나치게 단순화되었지만 궁금한). 며칠간 읽은 후 세션 인증이 덜 위험하다고 결정했습니다.

SPA와 함께 세션을 사용하는 것이 간단 해 보일 수 있지만 내장 Django 세션 인증은 주로 다른 도메인에서 실행되는 SPA가 아닌 Django 템플릿을 사용하는 웹 앱용으로 설계되었습니다. 세션 인증에 사용되는 CSRF- 토큰은 특히 다른 도메인에서 들어오는 인증 된 요청을 방지하기위한 것이므로 SPA를 인증하려는 사람에게 골칫거리가됩니다. django-cors-headers와 씨름하고 장고가 안전한 방법으로 CSRF 토큰을 다른 도메인에 설정하도록 시도 할 수있을만큼 용감하다면 최선을 다하길 바랍니다. 행운이 없었습니다. 이 접근 방식은 쿠키 및 보안과 관련된 많은 Django 설정을 완화해야하므로 위험 해 보였습니다.

Django Rest Framework 문서에는 세션으로 요청하기가 언급되어 있습니다. 인증 섹션으로 이동하면 세션 인증을 사용하여 요청을 허용하는 방법을 찾을 수 있지만 나머지 프레임 워크에 대한 크레딧을 제공하기 위해 SPA에 로그인하는 방법에 대한 언급은 없을 것입니다. 놀라운 문서. 나는 당황스러워했다.

내가 찾은 동일한 도메인에서 React와 Django를 제공하는 가장 일반적인 접근 방식은 Django의 정적 파일을 통해 React의 프로덕션 빌드를 제공하는 것입니다. 이 전략을 사용하여 몇 주 동안 개발 구성을 실행했지만 코드가 변경 될 때마다 새 프로덕션 빌드를 빌드하고 Django 서버를 다시 시작해야하는 것은 누구나 미치게 만들 수 있습니다.

내가 찾은 더 나은 솔루션은 Nginx를 역방향 프록시로 사용하여 동일한 도메인에서 Django와 React를 제공하는 것입니다. Docker를 사용하여 React, Django 및 Nginx에 대한 별도의 컨테이너를 가동했습니다. docker와 함께 볼륨을 사용하면 React와 Django에서 핫 리로딩을 계속 활용할 수 있습니다.

여기에서이 프로젝트의 코드 찾기

https://github.com/kieronjmckenna/DRF-React-Auth.git

Django 및 React 프로젝트를 설정해 보겠습니다.

Django 설정

세 개의 컨테이너에 대한 기본 폴더를 설정하십시오.

mkdir authTesting && cd authTesting

나중에 살펴볼 docker-compose.yaml 파일을 추가해 보겠습니다.

touch docker-compose.yaml

아직 가지고 있지 않다면 장고를 전역 적으로 설치하십시오. 우리는 django-admin을 활용하여 프로젝트를 시작할 수 있도록 이렇게 할 것입니다.

pip install django

django-admin startproject authDjango && cd authDjango

다시 말하지만, 여기에서는 pipenv를 사용하여 내 종속성을 관리 할 것입니다. 익숙하지 않은 경우 문서가 훌륭합니다.

pipenv install django djangorestframework psycopg2-binary

다음을 사용하여 가상 환경 셸을 시작합니다.

pipenv shell

프로젝트에 앱 추가

django-admin startapp authApp

settings.py의 앱에 authApp 및 rest_framework 추가

경로 — /authDjango/authDjango/settings.py

...
INSTALLED_APPS = [
...,
    
    'rest_framework',
'authApp'
]

postgres 도커 컨테이너로 데이터를 유지할 운이 없었기 때문에 로컬 컴퓨터에 postgres를 설정하고 데이터베이스 이름을 authTesting으로 지정합니다.

settings.py에서 postgres에 연결

경로 — /authDjango/authDjango/settings.py

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'authTesting',
'USER': 'postgres',
'PASSWORD': '...',
'HOST': 'host.docker.internal',
'PORT': 5433,
}
}

장고 Dockerfile

컨테이너 외부에서 서버가 제대로 실행되는지 자유롭게 테스트 해보세요. 도커에서 django를 바로 실행할 것입니다.

touch Dockerfile

Dockerfile을 설정할 수 있습니다. Docker에는 Django 설정에 대한 훌륭한 페이지가 있습니다 .

경로 — / authDjango / Dockerfile

FROM python:3.8.3
ENV PYTHONUNBUFFERED 1

RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/

pip freeze > requirements.txt

어떤 의미에서 컨테이너는 자체 가상 환경이기 때문에 Docker 컨테이너에 pipenv를 설정할 필요가 없습니다.

Docker Compose — Django

루트 디렉토리에 docker-compose 백업을 설정할 수 있습니다.

경로 — /docker-compose.yaml

version: '3.7'

services:
auth-django:
tty: true
container_name: auth-django
build: ./authDjango
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./authDjango:/code
ports:
- 8000:8000

이 프로젝트의 구조는 Wes Doyle의 멋진 YouTube 동영상에서 비롯되었습니다.

이 비디오는 두 개의 Flask 앱과 Vue를 함께 실행하는 것에 관한 것입니다. 분명히 시계를 주면, 그는 docker-compose, Dockerfile 및 Nginx를 설명하는 훌륭한 작업을 수행합니다. 이 비디오는 저에게 큰 도움이되었습니다.

컨테이너를 빌드하고 서버를 시작하겠습니다. 루트 디렉터리 (docker-compose-yaml이있는 위치)에서이 명령을 실행합니다.

docker-compose build

docker-compose up

localhost : 8000에서 :

Django 홈 페이지

바라건대 서버가 컴퓨터에서 실행되기를 바랍니다.

계속 진행하기 전에 마이그레이션하십시오.

docker-compose run auth-django python manage.py migrate

Django 및 DRF 인증,보기, URL

이제 Django 프로젝트로 돌아가서 인증을 설정하고 테스트 할 보호 된 DRF 끝점을 가져옵니다.

DRF 설정

settings.py에 다음 코드를 추가하십시오.

경로 — /authDjango/authDjango/settings.py

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication'
),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated'
]
}

이제 csrf 토큰 세트를 가져오고 로그인 해 보겠습니다.

임영 강 글을 꼭 읽어주세요. 이 시스템을 구성 할 때 큰 자원이었습니다. 아래의 set_csrf_token 및 login_view 뷰에 대한 코드는 그의 기사에서 직접 가져온 것입니다.

경로 — /authDjango/authApp/views.py

"""
SOURCE - https://yoongkang.com/blog/cookie-based-authentication-spa-django/
"""
import json
from django.contrib.auth import authenticate, login
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import JsonResponse


@ensure_csrf_cookie
def set_csrf_token(request):
    """
    This will be `/api/set-csrf-cookie/` on `urls.py`
    """
    return JsonResponse({"details": "CSRF cookie set"})


@require_POST
def login_view(request):
    """
    This will be `/api/login/` on `urls.py`
    """
    data = json.loads(request.body)
    username = data.get('username')
    password = data.get('password')
    if username is None or password is None:
        return JsonResponse({
            "errors": {
                "__all__": "Please enter both username and password"
            }
        }, status=400)
    user = authenticate(username=username, password=password)
    if user is not None:
        login(request, user)
        return JsonResponse({"detail": "Success"})
    return JsonResponse(
        {"detail": "Invalid credentials"},
        status=400,
    )

더미 APIView 엔드 포인트도 만들 수 있습니다.

경로 — /authDjango/auth/views.py

...
from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication
from rest_framework.response import Response

class CheckAuth(APIView):
authentication_classes = [SessionAuthentication]
def get(self, request):
return Response({'detail': 'You\'re Authenticated'})

/authDjango/authDjango/urls.py로 이동하십시오.

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('api/admin/', admin.site.urls),
path('api/', include('authApp.urls'))
]

/ authDjango / auth 폴더로 다시 이동합니다.

touch urls.py

경로 — /authDjango/auth/urls.py

from django.urls import path
from .views import set_csrf_token, login_view, CheckAuth
urlpatterns = [
path('set-csrf/', set_csrf_token, name='Set-CSRF'),
path('login/', login_view, name='Login'),
path('test-auth/', CheckAuth.as_view(), name='Test-Auth')
]

루트 디렉터리로 다시 이동합니다 (제 경우에는 인증 테스트).

앞서 말했듯이 노드가 설정되었다고 가정하겠습니다.

npx create-react-app auth-react

cd auth-react && touch Dockerfile

React Dockerfile

이제 React 프로젝트를위한 컨테이너를 설정하겠습니다.

경로 — / auth-react / Dockerfile

FROM node:latest
WORKDIR /code
COPY package*.json /code/
RUN npm install
COPY . /code/

docker-compose를 사용하여 React 서버를 실행합시다.

경로 — /docker-compose.yaml

version: '3.7'
services:
auth-django:
tty: true
container_name: auth-django
build: ./authDjango
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./authDjango:/code
ports:
- 8000:8000
# New React configuration
  auth-react:
tty: true
container_name: auth-react
build: ./auth-react
command: npm start
ports:
- 3000:3000
image: no-react
volumes:
- ./auth-react:/code

docker-compose build

docker-compose up

머리가 로컬 호스트 : 3000 / , 우리는 볼 수

그래서 우리는 Django와 React가 개발 빌드와 동시에 실행되지만 여전히 다른 도메인에 있습니다. 여기서 Nginx를 역방향 프록시로 사용할 것입니다.

Nginx

루트 디렉토리에서

mkdir auth-nginx && cd auth-nginx

touch Dockerfile nginx.conf

Nginx Dockerfile

다음에 nginx 컨테이너를 설정하겠습니다.

경로 — / auth-nginx / Dockerfile

FROM nginx:latest
COPY ./nginx.conf /etc/nginx/nginx.conf

Nginx 구성

nginx에 익숙하지 않다면 위로 스크롤하여 Wes Doyle이 링크 한 비디오를 보러 가십시오 . 그는이 기사에서 할 수있는 것보다 더 잘 설명합니다. 여기에있는 nginx.conf는 그가 설정 한 것과 유사합니다.

경로 — /auth-nginx/nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
upstream auth-django {
server auth-django:8000;
}
upstream auth-react {
server auth-react:3000;
}
server {
listen 80;
server_name localhost 127.0.0.1;
location /api {
proxy_pass              http://auth-django;
proxy_set_header        X-Forwarded-For $remote_addr;
}
location / {
proxy_pass              http://auth-react;
proxy_set_header        X-Forwarded-For $remote_addr;
}
}
}

Django Urls를 설정할 때 언급했듯이 / api의 모든 경로는 Django 서버로 전달됩니다. 다른 모든 것은 React로 보내질 것입니다.

Docker Compose — Nginx (Django 및 React 서비스 일부 변경 포함)

이제 docker-compose에게 Nginx 컨테이너를 실행하도록 지시하고 auth-django 및 auth-react 서비스를 약간 변경해야합니다.

version: '3.7'
services:
# NEW
reverse_proxy:
container_name: auth-nginx
build: ./auth-nginx
volumes:
- ./auth-nginx/nginx.conf:/etc/nginx/nginx.conf
ports:
- 80:80
depends_on:
- auth-django
- auth-react
auth-django:
tty: true
container_name: auth-django
build: ./authDjango
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./authDjango:/code
# ports:
#   - 8000:8000
# NEW
expose:
- 8000
auth-react:
tty: true
container_name: auth-react
build: ./auth-react
command: npm start
# ports:
#  - 3000:3000
# NEW
expose:
- 3000
image: no-react
volumes:
- ./auth-react:/code

포트는 컨테이너의 포트를 호스트 시스템의 포트에 매핑하는 데 사용됩니다. 반면 노출은 다른 도커 컨테이너가 원하는 컨테이너의 "노출 된"포트에 액세스 할 수 있도록하는 데 사용됩니다. 우리의 경우 nginx에게 auth-django 및 auth-django 컨테이너의 각 포트를 살펴 보도록 지시했습니다. 그래서 우리는 포트보다는 노출을 사용합니다.

Django 설정에 대한 빠른 변경

또한 일부 Django 설정을 변경해야합니다.

경로 — /authDjango/authDjango/settings.py


STATIC_URL = ‘/api/static/'
ALLOWED_HOSTS = ['*']

운영

docker-compose build

docker-compose up

이제 localhost /의 React 앱과 localhost / api의 Django 앱에 액세스 할 수 있습니다.

React에서 인증을받을 수 있습니다.

반응 앱

/ auth-react에서 반응 프로젝트로 이동

Axios 설정

npm install axios

cd src && touch axiosConfig.js

이제 React 앱을 설정하고 axios를 설치했습니다. axios 구성을 변경해야합니다. Axios에는 모든 요청에 ​​적용 할 수있는 기본값이 있습니다. http 요청을 보내기 위해 axios를 사용할 필요는 없지만 이러한 기본 구성이 있으므로 모든 요청에서 인증 헤더를 처리 할 필요가 없습니다. 제 생각에는 가치가 있습니다.

경로 — /auth-react/axiosConfig.js

import axios from 'axios'
axios.defaults.xsrfHeaderName = "X-CSRFToken"
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.withCredentials = true
export default axios

1) 장고로 보낼 CSRF 헤더의 이름은 'X_CSRFToken'입니다.

2) 토큰은 'csrftoken'으로 브라우저에 저장됩니다.

3) 기본적으로 CSRF 토큰과 같은 요청과 함께 자격 증명을 보내야하며, 일단 인증되면 세션 쿠키가 필요합니다.

4)이 설정이 변경된 axios 내보내기, 패키지에서 직접 axios를 가져 오는 대신 프로젝트 전체에 걸쳐이 파일에서 axios를 가져옵니다.

요구하다

저는 React 설정을 간단하게 유지하고 후크를 배우는 과정에 있으므로 클래스를 사용할 것이며 App.js에 모두 보관할 수있는 충분한 양의 코드가 있다고 생각합니다.

경로 — /auth-react/src/App.js

import React, { Component } from 'react';
import './App.css';
// Notice we're importing from the file we created, not the axios package
import axios from './axiosConfig';
export default class App extends Component {
constructor(props){
super(props)
this.state = {
username: '',
password: '',
auth: null,
endpoint: null
}
}
setCSRF = () => {
axios.get('api/set-csrf/').then(res => console.log(res))
}
handleChange = (e) => {
this.setState({[e.target.name]: e.target.value})
}
handleSubmit = (event) => {
event.preventDefault();
axios.post('/api/login/',
{username: this.state.username,
password: this.state.password}
).then(res => {
this.setState({auth: true})
}).catch(res => this.setState({auth: false}))
}
testEndpoint = () => {
axios.get('/api/test-auth/').then(res => this.setState(
  {endpoint:      true}))
  .catch(res => this.setState({endpoint: false}))
}
render() {
return <div style={{marginLeft: '20px'}}>
<div style={{height: '50px'}}></div>
<button onClick={this.setCSRF}>Set CSRF Token</button>
<div style={{height: '50px'}}></div>
<form onSubmit={this.handleSubmit}>
<label>Username</label>
<input name='username' value={this.state.username} onChange={this.handleChange}></input>
<label >Password</label>
<input name='password' value={this.state.password} onChange={this.handleChange}></input>
<input type='submit' value='Login'></input>
</form>
<div style={{height: '50px'}}></div>
<div>
{this.state.auth === null ? '' : (this.state.auth ? 'Login successful' : 'Login Failed' )}
</div>
<div style={{height: '50px'}}></div>
<button onClick={this.testEndpoint}>Test Endpoint</button>
<div>{this.state.endpoint === null ? '' : (this.state.endpoint ? 'Successful Request' : 'Request Rejected')}</div>
</div>
};
}
  1. CSRF 토큰을 설정하는 끝점에 도달하는 버튼입니다. 작업중인 프로젝트에서 App.js componentDidMount 함수의 / api / set-csrf /에 요청을 넣었습니다. 이렇게하면 누군가가 React App을 시작할 때마다 CSRF 토큰이 설정되었는지 확인합니다.
  2. 로그인 할 양식입니다. (사용자가 로그인 할 수 있도록 'docker-compose run auth-django python manage.py createsuperuser'실행). CSRF 설정 버튼을 누르지 않고 로그인을 시도하면 요청이 실패합니다.
  3. DRF APIView 엔드 포인트에 요청을 보내는 버튼입니다. 인증되지 않으면 거부됩니다.

다음으로 컨테이너를 다시 시작하십시오.

docker-compose build

docker-compose up

이를 통해 React App에서 Django Rest Framework 백엔드로 세션 인증을 사용하는 기능 개발 설정이 있습니다. 나는 단어의 개발을 강조해야한다 아무도 이 코드의 생산에 적합하지 않으며, 당신이 그것을 사용하도록 선택하면 내가 책임지지 않습니다.

이 코드가 유용한 것은 React와 Django의 핫 리로딩 기능을 활용하면서 동일한 도메인에서 서비스를 제공하므로 세션을 사용할 수 있다는 것입니다. 변경 사항을 확인하기 위해 웹 페이지를 다시로드해야한다는 점을 감안할 때 React의 새로운 프로덕션 빌드를 만들고 Django 서버를 다시 시작하는 것에 비해 두 가지 악이 적습니다.

Django 정적을 통해 React를 제공하는 방법을 보려면 CodingEntrepreneurs 비디오를 확인하세요.

그러나 그는 개발 영역을 설정하는 동안 프로덕션 빌드를 기다리는 것이 지루할 수 있다고 말합니다.

이 기사가 도움이 되었기를 바랍니다. React 앱에서 사용자 인증을 시도했을 때 확실히 간단하지 않았기 때문에 작성했습니다. 여기에있는 코드는 프로젝트 설정을위한 최소한의 코드입니다. 앞으로이 옵션의 보안을 조사하고 프로덕션을 준비 할 것입니다.

읽어 주셔서 감사합니다!

Suggested posts

서버 측 렌더링과 클라이언트 측 렌더링

서버 측 렌더링과 클라이언트 측 렌더링

당신은 몇 살입니까? 인터넷이 정적이고 비 동적이며 부실했던 때를 기억한다면 대답은 좋은 지표가 될 것입니다. 제가 90 년대에 어렸을 때 인터넷은 정보와 많은 상호 작용을 허용하는 대신 정보를 보여줄 수있었습니다.

Google Cloud에서 Shiny 대시 보드를 Dockerizing 및 배포

Shiny를 클라우드로 가져 오기위한 단계별 가이드

Google Cloud에서 Shiny 대시 보드를 Dockerizing 및 배포

Shiny 앱 및 대시 보드는 최종 사용자가 다양한 방식으로 데이터와 상호 작용할 수있는 매우 다재다능한 도구이며 앱 기능은 백그라운드에서 실행되는 R 코드에서 지원됩니다. 그러나 다른 사람들이 귀하의 작업에 액세스하고 혜택을 받으려면 응용 프로그램을 배포 할 위치를 생각해야합니다.

Related posts

성능 최적화 된 A / B 테스트 솔루션

성능 최적화 된 A / B 테스트 솔루션

의제 : 소개 : TL;하지만 읽을 수 있습니다. A / B 테스트, CloudFront 및 Lamba @ edge에 대해 이미 알고있는 경우 AWS Lambda @ edge를 사용한 A / B 테스트로 직접 이동하십시오. A / B 테스트 란 무엇입니까? A / B 테스트는 웹 사이트의 두 가지 버전에 대한 사용자의 참여를 비교하는 데 초점을 맞춘 UX 연구 방법론입니다.

fp-ts (Typescript)에서 Option 및 둘 중 하나 사용

저는 함수형 프로그래밍을 좋아합니다. 몇 년 동안 실수를하거나 토끼 구멍을 뚫는 것으로부터 저를 몇 번 구해 주었기 때문입니다. 동일한 입력이 주어지면 출력이 항상 동일하다는 것을 알면 안심입니다.

Syncfusion Blazor 파일 업로드 구성 요소에서 이미지를 미리 보는 방법

Syncfusion Blazor 파일 업로드 구성 요소에서 이미지를 미리 보는 방법

Syncfusion Blazor 파일 업로드는 하나 이상의 파일, 이미지, 문서, 오디오, 비디오 및 기타 파일을 서버에 업로드하기위한 구성 요소입니다. 여러 파일 선택, 진행률 표시 줄, 자동 업로드, 끌어서 놓기, 폴더 (디렉터리) 업로드, 파일을 포함하는 다양한 기능을 갖춘 HTML5 업로드 구성 요소 (<input type =”file”>)의 확장 버전입니다. 검증 등.

6 React 개발자로서 후회

내가 일찍했으면하는 것

6 React 개발자로서 후회

React는 배울 수있는 훌륭한 도구입니다. 그것은 우리가 우리 자신의 방식으로 일을 할 수있게합니다.