https://github.com/InhaBas/Inhabas.com/discussions/77
새로운 권한설계 고민 · Discussion #77 · InhaBas/Inhabas.com
— 기존 권한 검사 문제점 — 유저마다 역할과 그룹이 있는데, 어떤 url 요청이 있을 때마다 해당 유저의 역할과 그룹을 확인하는 함수를 이용했다. 권한 관련 설정이 초기에 계획했던 것보다 더
github.com
— 기존 권한 검사 문제점 —
유저마다 역할과 그룹이 있는데,
어떤 url 요청이 있을 때마다 해당 유저의 역할과 그룹을 확인하는 함수를 이용했다.
권한 관련 설정이 초기에 계획했던 것보다 더 복잡해지면서,
if 문으로 추가되는 코드량이 점점 많아졌다.
현재는 유저 역할이나 그룹에 대한 변경이 필요할 시에,
거의 변경 불가능한 코드가 되어버렸다.
아래 아주 간단한 코드 예시..
— 생각한 대안 —
- 홈페이지에서 제공하는 서비스 기능을 최소단위로 나누어, 해당 기능에 대한 CRUD 권한을 각각 설정한다.
- 유저 그룹 테이블과 서비스 권한 테이블을 매핑한다.
- 유저 역할 테이블과 서비스 권한 테이블을 매핑한다.
- 개인 유저 테이블과 서비스 권한 테이블을 매핑한다.
- 개인별 권한 >> 유저 역할별 권한 >> 유저 그룹별 권한, 과 같은 형태로 권한 계층 관계가 존재한다.
- 클라이언트 요청 처리 시에, 로그인한 유저의 (그룹 권한+역할 권한+개인 권한)을 종합하여 해당 서비스 제공 여부를 결정한다.
— 구현 방법 —
1. 기존 User 모델을 장고에서 제공하는 기본 User 모델(auth_user)과 연동
— 연동하기로 결정한 이유 —
원래는 테이블을 새로 다 설계한 후, 서비스별 권한을 세분화하여 테이블을 작성해야한다. 그런데 django-auth 에서 앱을 생성할 때마다 auth_permission 이라는 테이블에 해당 권한을 자동으로 추가해놨던 것을 발견했다.
처음 이 서비스를 개발했던 선배님께서는 django 에서 제공하는 기본 User model 를 사용하지 않고, 개별적인 User model 을 생성해서 사용했다. 중간부터 개발에 참여했어서 아무생각 없이 user 모델을 사용했었는데, 장고에서 제공하는 user모델을 사용하게 되면 user 모델과 연동해서 제공하는 기능들이 꽤 있었다. 더 알아보니 권한 관련 기능도 제공하고 있었다. auth_permission 테이블의 정체가 그 기능을 위한 것 중 하나였다.
현재 프론트엔드에서는 vue.js 를 도입하는 중에 있어서 본격적인 개발이 진행되기 전에 권한 관련 문제를 해결해야했다. 새로 바닥부터 설계하고 구현하려면 너무 많은 시간이 들어가니 장고 퍼미션 기능을 사용하기로 했다. 장고 퍼미션을 사용하려면 먼저 기존의 User 모델을 장고의 User 모델과 연동시켜야만 한다. 주의해야할 점은 기존에 배포하던 서비스에 문제가 생기지 않도록 해야 한다는 것. 기존 db 테이블 컬럼이나 레거시 코드를 직접적으로 수정하면 안되고, 새로운 코드를 추가하여 테스트 후에 점진적으로 교체해야함.
— 구현 —
연동 전략을 고민하는 데 있어서 중요하게 고민했던 지점은 3가지이다.
- 레거시 코드를 당장에 수정하지 않아도 되는가?
- django-allauth 소셜 로그인 기능이 유지 되는가? (django allauth 가 장고에서 기본 제공하는 user 모델을 기반으로 하기 때문.)
- 성능에 이슈가 있는가?
가능한 두 가지 방법을 생각해보았다.
1. 사용중인 User 모델이 AbstractBaseUser 상속
- (1번 ok..?)
: 기존 user table column을 변경하지 않고, password / last_login / is_active / user_id / email 컴럼만 추가하면 됨. 하지만 우려되는 점은 하단 참고.
- (2번 ok..?)
: 위에서 언급한 컬럼들 추가하면 됨.
다만 현재 학번이 pk로 잡혀있어서 pk를 풀고 non-clustered index & null 처리 해야함.
소셜로그인 순서 상, 소셜로그인 정보가 테이블에 먼저 저장되고 그 후에 사용자가 직접 입력한 정보들이 저장되기 때문에
학번이 pk 로 잡혀있으면 무결성에 위배되는 상황이 발생.
따라서 기존에 views.py 에서 user 모델을 fetch 할 때, pk 키워드를 썼던 코드들을 학번 컬럼 이름으로 변경해주어야한다.
- (3번 ok)
: 다른 테이블에서 학번을 기준으로 참조하고 있다.
조회가 빈번한 학번 column 이 non-clustering index 가 될 경우 clustered index에 비해 조회 속도가 저하될 우려가 있지만,
동아리 홈페이지 특성상 눈에 띄는 속도저하가 발생될 만큼의 데이터가 쌓이지 않을 것.
- (논외로 우려되는 점)
: user table 의 크기가 커지게 되는데, 이는 db 정규화에 위배되는 요소. 나중에 추가해야할 정보들이 있다면 점점 더 커지게 될 수도,,
2. 장고 유저 모델(auth_user) & 현재 사용중인 유저 모델(user_profile) 1대 1 매핑
- 장고에서 제공하는 기본 유저 모델을 auth_user, 현재 사용 중인 유저 모델을 user_profile 이라고 하고 학번을 기준으로 1대1 매핑
- (1번 ok)
: 기존의 유저 모델과 장고 유저모델 둘 다 해치지 않는다. 그대로 사용 가능
- (2번 ok)
: 장고 유저모델로 그대로 소셜 로그인 사용가능
- (3번 ok)
: 권한 검사를 하려면 auth_user 테이블을 사용해야 한다. 하지만 현재 유저에 관한 모든 로직은 user_profile 로 이루어지고 있다.
결국 해당 학생의 학번으로 auth_user 테이블에 접근하고 관련 권한 검사를 실시해야한다.
추가적인 db join select 등이 이루어지겠지만 세션 캐싱을 통해서 매번 db에 접근하는 것을 막을 수 있다.
위의 내용을 토대로 판단하여, 두번째 방법을 선택했다.
2. 커스텀 backend.py 구현
— 배경 —
장고의 권한 검사는 django.contrib.auth 에서 담당한다. 여러 기능들이 있지만 결국 최종적인 권한 검사는 ModelBackend 클래스에서 담당한다. 유저 또는 유저그룹에서 접근 가능한 서비스 목록에, 인자로 넘겨받은 서비스 이름이 존재하면 통과한다. 접근 가능한 서비스 목록을 검사하는 중에 캐쉬되어 있는지 먼저 확인하고, 없으면 db를 확인한다. db 확인 후에는 캐싱 후에 반환한다. _get_permissions 라는 함수가 그런 역할을 한다.
캐시하는 방식이 인상 깊었는데, user._user_perm_cache = ['board_view', 'board_create', ] 이렇게 단순히 메모리에 저장하는 방식으로 구현하고 있었다. 동아리 홈페이지 같이 동시접속자가 매우 낮은 환경에서는 간단하고 매우 좋은 방법이다!! 이 방식은 힙 메모리에 캐싱하는 방식! 다른 캐시 db 들은 어떤 식으로 캐싱하는 지 궁금해졌다.
어쨌든 django.contrib.auth.models 에 정의된 테이블을 기반으로 ModelBackend 가 권한 검사를 수행한다는 것을 파악했다. 하지만 우리는 user group 말고도 user role 이 따로 존재한다. auth.models 에는 user&group&permission 간 m2m 필드를 설정되어 있다. role 검사를 추가적으로 하기 위해서는 user&role&auth.models.permission 간 m2m 필드를 설정하고, ModelBackend 에서 role 권한 검사 로직을 추가해줘야 한다.
— 구현 —
- 새로운 permission 앱을 생성
- models.py 에 role & user & permission 을 m2m 지정.
- db 마이그레이션
- backends.py 에 ModelsBackend 를 상속받는 새로운 Backend를 생성, => role 검사 로직 추가
- settings 에 AUTHENTICATION_BACKENDS 를 새로 만든 Backend 로 지정
진행사항 (2021-11-14 #89)
- db 권한을 실제 설정하다보니, 기존에 유지하고 있던 role 과 group을 합쳐도 상관없겠다고 생각했음. role 과 group 에 속한 계급(?)이 겹치는 부분 없이 다 나누어 떨어짐.
- 따라서 필요없다고 판단하여 custom backend.py를 지웠음. (실제로 구현까지 하고 작동 확인했었다... 맴찢...)
- db 에 현재 개발 진행되고 있는 게시판 부분에 대하여 새롭게 권한을 적용했다.
- db 변경사항 sql 파일로 각자 로컬에서 테스트 부탁
'웹 프로젝트 (IBAS) > Django 레거시' 카테고리의 다른 글
[Django 웹 프로젝트] 9. 소셜 로그인 관련 오류 수정 (2022-03-13) (0) | 2022.03.13 |
---|---|
[Django 웹 프로젝트] 8. 점진적으로 api 로 교체 가능? (2021-11-21) (0) | 2022.03.13 |
[Django 웹 프로젝트] 6. 유지 보수를 위한 새로운 아키텍처 고민 (2021-10-21) (0) | 2022.03.01 |
[Django 웹 프로젝트] 5. static file name hashing 하기 (2021-09-09) (0) | 2022.02.28 |
[Django 웹 프로젝트] 4. 댓글(vue.js)을 django 에 붙이기 (2021-08-03) (0) | 2022.02.28 |