Django 7. 기본 개념 6(역할기반 허가&인증)

jiffydev·2020년 10월 7일
0

1. User Role Based Permissions & Authentication

웹사이트에는 사용자와 관리자가 있다. 이 두 그룹이 사용하는 페이지는 같은 부분도 있겠지만 관리자만이 사용하는 페이지도 존재할 것이다. EC사이트를 예로 들면 모든 주문에 대한 배송정보, 고객관리 정보 등의 화면은 일반 유저는 접근할 수 없도록 해야 한다.

admin페이지에 가면 AUTHENTICATION AND AUTHORIZATION 항목에 Groups Users 카테고리가 존재하는 것을 볼 수있다. 여기서 Groups항목은 말 그대로 유저를 어떤 그룹에 소속시킬 것인지를 결정하는 곳인데, django.contrib.auth.models안에 그룹 모델이 존재하여 name속성으로 생성한 그룹의 이름을 찾을 수 있다.(예: admin그룹, customer그룹)

def registerPage(request):
    if request.user.is_authenticated:
        return redirect('home')
    else:
      form=CreateUserForm()
      if request.method=="POST":
          form=CreateUserForm(request.POST)
	...

def loginPage(request):
    if request.user.is_authenticated:
        return redirect('home')
    else:
      if request.method=="POST":
          username=request.POST.get('username')
          password=request.POST.get('password')
          user=authenticate(request, username=username, password=password)
      ....

역할기반 페이지를 구성하기 전에, 위와같이 이미 로그인한 유저가 로그인 페이지/회원가입 페이지에 진입하면 자동으로 메인페이지로 이동시키는 코드가 있다. 두 함수가 모두 같은 내용으로 되어 있으므로 중복을 최대한 줄이기 위해 인증된 유저인지 확인하는 절차를 데코레이터함수로 바꾸고자 한다.

이를 위해 새로 decorators.py 파일을 새로 생성하고 아래와 같이 데코레이터 함수를 만든다.

# decorators.py

from django.http import HttpResponse
from django.shortcuts import redirect

def unauthenticated_user(view_func):
    def wrapper_func(request, *args, **kwargs):
        if request.user.is_authenticated:
            return redirect('home')
        else:
            return view_func(request, *args, **kwargs)
    return wrapper_func

데코레이터에 관한 상세한 설명은 공식문서를 참조하도록 하고, 간단히 설명하자면 어떤 함수를 실행하기 전에 데코레이터 함수를 실행하여 조건을 확인하고, 조건에 부합하는 경우만 메인 함수를 실행하도록 하는 함수이다.
이처럼 데코레이터를 사용하면 is_authenticated 관련 코드를 반복해서 적을 필요 없이, 그러면서도 다른 함수에서도 인증된 유저를 간단하게 확인할 수 있다.
데코레이터로 함수를 사용하기 위해 view에서 decorators를 import 하고 데코레이터에 넣을 함수 위에는 @unauthenticated_user를 붙여주면 끝이다.

본격적으로 유저 권한에 따라 페이지 접근을 나누기 위해, 우선 관리자 그룹이 아닌 유저를 차단하는 함수를 작성한다. 이 함수도 뷰의 다른 여러 함수에 적용될 것이기 때문에 데코레이터 파일에 작성한다.

# decorators.py
...
# 처음 함수는 유저 권한을 받아오는 역할만 한다.
def allowed_users(allowed_roles=[]):
    # 실질적인 데코레이터의 기능은 이 함수가 담당한다.
    def decorator(view_func):
        def wrapper_func(request, *args, **kwargs):
            group=None
            if request.user.groups.exists():
                group=request.user.groups.all()[0].name
            if group in allowed_roles:
                return view_func(request, *args, **kwargs)
            else:
                return HttpResponse('You are not authorized to view this page')
        return wrapper_func
    return decorator

# views.py
@login_required(login_url='login')
@allowed_users(allowed_roles=['admin'])
def updateOrder(request,pk):
    order=Order.objects.get(id=pk)
    form=OrderForm(instance=order)

    if request.method == "POST":
        #print('printing post:', request.POST)
        form=OrderForm(request.POST, instance=order)
        ...

처음 함수에서 우선 allowed_roles=[admin]을 인자로 받고, 다음 decorator에서 주문 갱신 관련 함수를 인자로 받게 된다. 그리고 요청 데이터의 그룹 속성을 확인해, admin이라면 그대로 주문 갱신 함수를 실행하고, 아니라면 허가되지 않았다는 메시지를 띄우고 종료한다.

또한 현재 메인페이지에는 모든 사용자의 정보와 모든 배송 관련 정보가 들어있다. 사용자에게는 본인의 정보와 관련된 페이지만 보여주고 싶으므로, 메인페이지에 들어갈 때 계정 권한을 확인하고 사용자라면 본인의 정보 페이지로 리디렉트 하도록 설정해야 한다.

# decorators.py
def admin_only(view_func):
    def wrapper_func(request, *args, **kwargs):
        group=None
        if request.user.groups.exists():
            group=request.user.groups.all()[0].name
        
        if group=='customer':
            return redirect('user')
        
        if group=='admin':
            return view_func(request, *args, **kwargs)
    return wrapper_func

# views.py
@login_required(login_url='login')
@admin_only
def home(request):
    orders=Order.objects.all()
    customers=Customer.objects.all()
    
    total_orders=orders.count()
    total_customers=customers.count()

    delivered=orders.filter(status='Delivered').count()
    pending=orders.filter(status='Pending').count()
    ...

위에서 했던 데코레이터와 유사하다. 유저가 메인페이지로 접속하여 home함수가 실행되기 전에, 데코레이터 함수에서 접속한 유저의 권한을 체크하고, customer에 속해있다면 user페이지로 리디렉트, admin이라면 home함수를 실행한다.

마지막으로, 앞으로 회원가입을 하는 사용자에게 권한을 부여하는 작업이 필요하다. 보통 admin 권한은 admin이 직접 부여하므로 함수로 만들지 않고, 일반 사용자에게 customer 권한을 부여하는 함수를 만들 것이다.

@unauthenticated_user
def registerPage(request):
    form=CreateUserForm()
    if request.method=="POST":
        form=CreateUserForm(request.POST)
        if form.is_valid():
            user=form.save()
            username=form.cleaned_data.get('username')
            group=Group.objects.get(name='customer')
            user.groups.add(group)
            messages.success(request, 'Account was created for ' + username)
            return redirect('login')

    context={'form':form}
    return render(request, 'accounts/register.html', context)

폼을 작성한 후, group변수에 customer 그룹의 객체 정보를 담는다. 이 정보를 폼을 작성한 유저의 그룹에 추가해줌으로써 가입단계에서 그룹이 설정될 수 있다.

profile
잘 & 열심히 살고싶은 개발자

0개의 댓글