Flask(웹 어플리케이션)과 Nginx(웹 서버)를 uWSGI(WSGI 인터페이스)를 이용하여 서로 연결하여 서비스를 할 수 있습니다.
WSGI(Web Server Gateway Interface)
- 웹서버와 웹 어플리케이션이 어떤 방식으로 통신하는가에 관한 인터페이스를 의미
- 웹서버와 웹어플리케이션 간의 소통을 정의해 어플리케이션과 서버가 독립적으로 운영될 수 있게 돕는다.
- WSGI 어플리케이션은 uWSGI라는 컨테이너에 담아 어플리케이션을 실행하게 되며, uWSGI가 각각의 웹서버와 소통하도록 설정하면 끝이다. Flask, django와 같은 프레임워크는 이미 WSGI 표준을 따르고 있기 때문에 바로 웹서버에 연결해 사용할 수 있다.
- WSGI는 파이썬 표준이며, 서버에서 들어온 요청에 맞춰서 파이썬 웹 어플리케이션에 미리 등록된 로직을 콜백 함수로 서버에 응답해줍니다. 여기서 WSGI 인터페이스에 맞춰서 제작된 프레임워크가 uWSGI입니다.
오래된 uwsgi를 삭제한다.
mv /usr/bin/uwsgi /usr/bin/uwsgi-old
새롭게 uwsgi를 설치해준다.
pip install uwsgi
ln -s /usr/local/bin/uwsgi /usr/bin/uwsgi
uwsgi로 해당 어플리케이션을 실행한다.
uwsgi -s /tmp/uwsgi.sock --module yourapplication --callable app --venv .venv
파라미터를 매번 입력하기 번거로우므로 다음과 같으 ini파일을 작성해 저장해 놓고 실행 할 수 있다.
[uwsgi]
chdir=/home/ubuntu/helloWorld
chmod-socket=666
callable=app
module=app
socket=/tmp/uwsgi.sock
virtualenv=/home/ubuntu/helloWorld/.venv
윗 처럼 작성한 후 다음 명령어로 실행한다.
uwsgi <filename> &
https://edykim.com/ko/post/connecting-flask-uwsgi-to-nginx/(출처)
nginx
- nginx는 기존의 apache같은 웹 서버입니다. 기존 apache 서버와 다른 점은 nginx 는비동기 이벤트 기반으로 만들어졌고, apache 서버는 동기식, 프로세스 또는 쓰레드 기반으로 만들어졌습니다. apache서버에 비해서메모리를 적게가져간다는 장점이 있습니다.
apt-get을 통해 설치
apt-get install nginx-full
/etc/nginx/sites-available/default 파일을 열어 설정을 해준다.
server {
listen 8080;
server_name helloworld.haruair.com;
location / {
try_files $uri @helloworld;
}
location @helloworld {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi.sock;
}
}
위 설정을 통해 nginx로 들어오는 모든 요청을 uWSGI로 보내고
또 돌려받아 nginx를 통해 클라이언트에 전달하게 된다. nginx를 재구동하면 적용된다.
/etc/init.d/nginx restart
- Python의 객체에 데이터 모델을 정의하고 이를 데이터베이스와 매핑해주는 것을 ORM(Object Relaition Model)이라고 합니다. 덕분에 코드는 특정 데이터베이스에 종속되지 않고, 기본 객체 만으로 데이터를 기술할 수 있기 때문에 조금 더 OOP 스러운 코드를 작성할 수 있습니다.
Python에서 ORM으로 많이 쓰이는 것 중 SQLAlchemy가 있는데, 이를 Flask에서 플러그인 처럼 사용하기 쉽게 만들어진 Flask-SQLALchemy가 있습니다.
패키지 설치
(venv) PS flask_blog> pip install flask_sqlalchemy
이것은 반환한다None(예외를 발생시키지ID가 유효하지 않은 경우).이 경우 세션에서 ID가 수동으로 제거되고 처리가 계속됩니다.
사용자 클래스
사용자를 나타내는 데 사용하는 클래스는 다음 속성 및 메서드를 구현해야합니다.
is_authenticatedTrue사용자가 인증 된 경우, 즉 유효한 자격 증명을 제공 한경우이 속성이 반환되어야합니다.인증 된 사용자 만의 기준을 충족합니다login_required.is_active이 속성은True활성 사용자 인 경우 인증되는 것 외에도 계정을 활성화했거나 일시 중지하지 않았거나 응용 프로그램이 계정을 거부 한 조건을반환해야합니다.비활성 계정은 로그인 할 수 없습니다 (물론 강제로).is_anonymousTrue익명 사용자 인 경우이속성이 반환되어야합니다.(실제 사용자는False대신돌아와야합니다.)get_id()이 메소드는unicode이 사용자를 고유하게 식별하는user_loader콜백을반환해야하며콜백에서 사용자를로드하는 데 사용할 수 있습니다.참고이이 것을해야한다일unicode- ID가 기본적 인 경우int또는 다른 유형, 당신은으로 변환해야합니다unicode.
사용자 클래스 구현을보다 쉽게하기 위해 from을 상속하면UserMixin이러한 모든 속성 및 메서드에 대한 기본 구현이 제공됩니다.(필요하지는 않습니다.)
@app.route('/login',methods=['GET','POST'])deflogin():# Here we use a class of some kind to represent and validate our# client-side form data. For example, WTForms is a library that will# handle this for us, and we use a custom LoginForm to validate.form=LoginForm()ifform.validate_on_submit():# Login and validate the user.# user should be an instance of your `User` classlogin_user(user)flask.flash('Logged in successfully.')next=flask.request.args.get('next')# is_safe_url should check if the url is safe for redirects.# See http://flask.pocoo.org/snippets/62/ for an example.ifnotis_safe_url(next):returnflask.abort(400)returnflask.redirect(nextorflask.url_for('index'))returnflask.render_template('login.html',form=form)
경고 :반드시next매개 변수값을 확인해야합니다.그렇지 않으면 응용 프로그램이 열린 리디렉션에 취약합니다.구현 예는이 Flask Snippet을is_safe_url참조하십시오.
그렇게 간단합니다.그런 다음current_user모든 템플릿에서 사용할 수있는프록시를 사용하여 로그인 한 사용자에게 액세스 할 수 있습니다.
{% if current_user.is_authenticated %} Hi {{ current_user.name }}! {% endif %}
@login_manager.unauthorized_handlerdefunauthorized():# do stuffreturna_response
인증 헤더를 사용하여 로그인
주의
이 방법은 더 이상 사용되지 않습니다.request_loader대신 아래를사용하십시오.
때로는AuthorizationAPI 요청과 같은 헤더를사용하여 기본 인증 로그인을 지원하려고합니다.헤더를 통한 로그인을 지원하려면header_loader콜백을 제공해야합니다.이 콜백은user_loader사용자 ID 대신 헤더 값을 허용한다는 점을 제외하면 콜백과 동일하게 작동해야합니다.예를 들면 다음과 같습니다.
기본적으로Authorization헤더 값은header_loader콜백으로전달됩니다.AUTH_HEADER_NAME구성에사용 된 헤더를 변경할 수 있습니다.
요청 로더를 사용한 사용자 정의 로그인
때로는 헤더 값이나 쿼리 인수로 전달 된 API 키와 같은 쿠키를 사용하지 않고 사용자를 로그인하려고합니다.이 경우request_loader콜백을사용해야합니다.이 콜백은user_loaderuser_id 대신 플라스크 요청을 수락한다는 점을 제외하면 콜백과 동일하게 작동해야합니다.
예를 들어,Authorization헤더를사용하여 URL 인수와 기본 인증 모두에서 로그인을 지원하려면다음을 수행하십시오.
@login_manager.request_loaderdefload_user_from_request(request):# first, try to login using the api_key url argapi_key=request.args.get('api_key')ifapi_key:user=User.query.filter_by(api_key=api_key).first()ifuser:returnuser# next, try to login using Basic Authapi_key=request.headers.get('Authorization')ifapi_key:api_key=api_key.replace('Basic ','',1)try:api_key=base64.b64decode(api_key)exceptTypeError:passuser=User.query.filter_by(api_key=api_key).first()ifuser:returnuser# finally, return None if both methods did not login the userreturnNone
익명 사용자에 대한 사용자 정의 요구 사항이있는 경우 (예 : 권한 필드가 필요함) 익명 사용자를 작성하는 호출 가능 (클래스 또는 팩토리 기능)을 제공 할 수 있습니다LoginManager.
login_manager.anonymous_user=MyAnonymousUser
나를 기억
기본적으로 사용자가 브라우저를 닫으면 플라스크 세션이 삭제되고 사용자가 로그 아웃됩니다."Remember Me"는 사용자가 브라우저를 닫을 때 실수로 로그 아웃되는 것을 방지합니다.이것은사용자가 로그 아웃 한 후 로그인 양식으로 사용자의 사용자 이름 또는 비밀번호를 기억하거나 미리 채우는 것을 의미하지는않습니다.
“Remember Me”기능은 구현하기 까다로울 수 있습니다.그러나, 플라스크 - 로그인은 거의 투명하게 - 그냥 통과remember=True받는login_user전화.쿠키는 사용자의 컴퓨터에 저장되며, Flask-Login은 해당 쿠키가 세션에없는 경우 해당 쿠키에서 자동으로 사용자 ID를 복원합니다.쿠키가 만료되기까지의 시간은REMEMBER_COOKIE_DURATION구성으로 설정하거나에 전달할 수 있습니다login_user.쿠키는 변조 방지 기능이므로 사용자가 쿠키를 변조 (예 : 다른 사람의 사용자 ID를 자신의 위치에 삽입)하는 경우 쿠키가 존재하지 않는 것처럼 쿠키가 거부됩니다.
이 수준의 기능은 자동으로 처리됩니다.그러나 애플리케이션이 중요한 데이터를 처리하는 경우 추가 인프라를 제공하여 기억 쿠키의 보안을 강화할 수 있습니다.
대체 토큰
사용자 토큰을 기억 토큰의 값으로 사용한다는 것은 로그인 세션을 무효화하기 위해 사용자의 ID를 변경해야한다는 것을 의미합니다.이를 개선하는 한 가지 방법은 사용자 ID 대신 다른 사용자 ID를 사용하는 것입니다.예를 들면 다음과 같습니다.
이렇게하면 사용자가 비밀번호를 변경할 때 사용자의 대체 ID를 임의로 생성 된 새 값으로 자유롭게 변경할 수 있으므로 이전 인증 세션이 유효하지 않게됩니다.대체 ID는 여전히 사용자를 고유하게 식별해야합니다. 두 번째 사용자 ID로 생각하십시오.
새로운 로그인
사용자가 로그인하면 해당 세션이 "새로 고침"으로 표시되어 실제로 해당 세션에서 인증되었음을 나타냅니다.세션이 삭제되고 "기억하기"쿠키로 다시 로그인하면 "비 신규"로 표시됩니다.login_required신선도를 구분하지 않으므로 대부분의 페이지에 적합합니다.그러나 개인 정보 변경과 같은 민감한 조치에는 새로 로그인해야합니다.(비밀번호 변경과 같은 작업은 항상 비밀번호를 다시 입력해야합니다.)
login_manager.refresh_view="accounts.reauthenticate"login_manager.needs_refresh_message=(u"To protect your account, please reauthenticate to access this page.")login_manager.needs_refresh_message_category="info"
또는 새로 고침을 처리하기 위해 고유 한 콜백을 제공하여 :
@login_manager.needs_refresh_handlerdefrefresh():# do stuffreturna_response
위의 기능은 쿠키 도둑으로부터“Remember Me”토큰을 보호하는 데 도움이되지만 세션 쿠키는 여전히 취약합니다.Flask-Login에는 세션 보호 기능이있어 사용자의 세션 도난을 방지 할 수 있습니다.
LoginManager및 앱구성에서 세션 보호를 구성 할 수 있습니다.활성화 된 경우basic또는strong모드에서 작동 할 수 있습니다.상의를 설정하려면LoginManager, 설정된session_protection속성을하는"basic"나"strong":
login_manager.session_protection="strong"
또는 비활성화하려면 :
login_manager.session_protection=None
기본적으로"basic"모드에서 활성화됩니다.그것은 설정하여 응용 프로그램의 설정에서 비활성화 할 수 있습니다SESSION_PROTECTION에 대한 설정을None,"basic"또는"strong".
세션 보호가 활성화되면 각 요청마다 사용자 컴퓨터의 식별자 (기본적으로 IP 주소 및 사용자 에이전트의 보안 해시)가 생성됩니다.세션에 연관된 식별자가 없으면 생성 된 식별자가 저장됩니다.식별자가 있고 생성 된 식별자와 일치하면 요청이 정상입니다.
식별자가basic모드에서일치하지 않거나세션이 영구적 일 경우 세션은 단순히 새로 고침되지 않은 것으로 표시되며 새로 로그인해야하는 모든 것 때문에 사용자가 다시 인증해야합니다.(물론, 유효한 경우 새 로그인을 사용하고 있어야합니다.)
strong비 영구 세션의 모드에서 식별자가 일치하지 않으면전체 세션 (및 기억 토큰이있는 경우)이 삭제됩니다.
API에 대한 세션 쿠키를 사용하지 않도록 설정
API 인증시 Flask Session 쿠키 설정을 비활성화 할 수 있습니다.이렇게하려면 요청에 설정 한 플래그에 따라 세션 저장을 건너 뛰는 사용자 정의 세션 인터페이스를 사용하십시오.예를 들면 다음과 같습니다.
fromflaskimportgfromflask.sessionsimportSecureCookieSessionInterfacefromflask_loginimportuser_loaded_from_headerclassCustomSessionInterface(SecureCookieSessionInterface):"""Prevent creating session from API requests."""defsave_session(self,*args,**kwargs):ifg.get('login_via_header'):returnreturnsuper(CustomSessionInterface,self).save_session(*args,**kwargs)app.session_interface=CustomSessionInterface()@user_loaded_from_header.connectdefuser_loaded_from_header(self,user=None):g.login_via_header=True
이렇게하면 사용자가를 사용하여 인증 할 때마다 플라스크 세션 쿠키를 설정할 수 없습니다header_loader.
현지화
기본적으로 사용자가 로그인해야 할 때 메시지를 표시하는LoginManager데 사용flash됩니다.이 메시지는 영어로되어 있습니다.당신은 현지화를 필요로하는 경우, 설정localize_callback의 속성LoginManager들이 전송되기 전에 이러한 메시지 호출 할 수있는 기능을flash예를 들어,gettext.이 함수는 메시지와 함께 호출되고flash대신반환 값이 전송됩니다.
로그인 페이지로 리디렉션하기위한 URL을 만듭니다.login_view제공되는경우URL 만 반환합니다.next_url그러나 제공되는경우next=URL로그인보기가 해당 URL로 다시 리디렉션 될 수 있도록 쿼리 문자열에 매개 변수가 추가됩니다.Flask-Login의 기본 무단 핸들러는 로그인 URL로 리디렉션 할 때이 기능을 사용합니다.사용 된 호스트 이름을 강제하려면 호스트로 설정하십시오FORCE_HOST_FOR_REDIRECTS.요청 헤더 Host 또는 X-Forwarded-For가있는 경우 외부 사이트로 리디렉션되지 않습니다.
매개 변수 :
login_view(str) – 로그인보기의 이름입니다.또는 로그인보기의 실제 URL입니다.
대단한건 아니고, flask 는 기본적으로 jinja2 를 템플릿 언어로 사용하는데 사용하다 보면 하나의 template에서 공통적으로 사용되어 지는 부분이 있다. 예를 들면, 같은 css 나, 자바스크립트를 가져오는 header의 부분이나 상단의 navigation 부분, 하단의 footer 부분이 그러한데 일일히 모든 템플릿에 넣어 주기는 귀찮다. 그래서 jinja2 에서는{% include %} 를 통해서 하나의 html 에서 다른 html 을 가져올수 있도록 해준다. 단순히 가져오는 것이라고 생각할수 있는데 내부적으로는 랜더링된 결과를 리턴한다고 한다.
의 예처럼 nav.html 에서는 status 라는 flask 로 부터 받아온 값으로 Login, Logout 을 보여줄지를 결정하는데(그리 좋은 예제는 아님) nav.html 에서 랜더링 된 html 이 include를 사용한 쪽에 포함되는 것이다.
include 문에는ignore missing 옵션이 있는데 해당 옵션은 말 그대로 없으면 무시해라라는 옵션이다. 위의 예에서 만약 nav.html 이 존재하지 않는다면, 호출한 페이지를 브라우저에서 열었을때 에러가 발생되고 flask에서는 nav.html이 없다는 에러 메시지가 출력이 된다. 그렇지만 아래처럼 설정하게 되면 없는 부분을 무시하고 나오게 된다.
is_authenticated는 위에서 살펴 본 is_anonymous와 반대된다고 생각하시면 됩니다. 저 코드와 반대로 로그인 여부를 묻는 것입니다. 만약 로그인 되어 있다면 이 코드는 True를 반환합니다. 그렇다면 이 코드가 views.py에서어떻게 사용되는지 알아볼까요?
1
2
3
4
5
6
if request.user.is_authenticated:
pass
# do something if user is logged in
else:
pass
# do something if user is logged_out
이렇게 위와는 반대로 사용되게 됩니다. 그렇다면 템플릿 태그는 어떨지 알아보겠습니다.
1
2
3
{% if user.is_authenticated %}
<p>이 유저는 로그인 되어 있습니다.</p>
{% endif %}
이렇게 사용되게 됩니다. 템플릿 태그를 이용하면 둘 중 하나만 알아도 대부분의 코드를 구현할 수 있을 것입니다.
1
2
3
4
5
{% if user.is_authenticated %}
<p>이 유저는 로그인 되어 있습니다.</p>
{% else %}
<p>이 유저는 로그아웃 되어 있습니다..</p>
{% endif %}
이렇게 사용하면 위에서 {% if user.is_authenticated %}가 False를 반환하게 될 경우 {% else %}안의 코드가 실행됩니다. 그러므로 한 가지만 알면 두 가지 경우를 다 처리할 수 있습니다.
render_templates 함수에 키 / 값 쌍을 추가하여 경로 처리기의 동적 내용을 템플릿에 전달할 수 있습니다. 위의 예에서 "pagetitle"및 "mycontent"변수는 렌더링 된 페이지에 포함되도록 템플리트에 전달됩니다. 템플릿에 다음 변수를 두 개의 중괄호로{{mytitle}}.{{mytitle}}