概要
- Djangoにて、ユーザー(グループ)ごとにページへのアクセス制限が設定し、アクセス権がない場合はフラッシュメッセージ(アラートみたいなポップアップ)を表示する実装を行いました。
- デフォルトで実装されているauth機能と、メッセージフレームワークを利用します。
- 本記事ではその実装方法を記載します。
前提
- ディレクトリ構造は以下の通りです(関係ないものは省略)。
app
├── app
│ ├── settings.py
│ └── etc...
├── home
│ ├── views.py
│ ├── urls.py
│ ├── etc...
│ └── templates/pages
│ ├── base.html
│ └── home.html
└── sample
│ ├── views.py
│ ├── urls.py
│ └── templates/sample
│ ├── base.html
│ └── sample.html
└── sample2 etc...
-
home.html
には、sample
やsample2
のボタンがあり、それをクリックするとそれぞれのhtml
に飛べる状態になっています。今回のアクセス制限では、ボタンを押してもユーザーが該当のグループに属していない場合はホーム画面にリダイレクトされる実装を行います。 - URLの直打ちでもルートにリダイレクトする設定になっています。
実装内容
①Auth機能でGroup作成&User追加
②settings.py
の編集
③urls.py
の編集
④views.py
の編集
⑤html
テンプレートの編集
①Auth機能でGroup作成&User追加
- Djangoの管理者アカウントでログインします。
- Admin画面から「Group」を選択すると、以下の画面に遷移されるので、任意の名前でグループを作成します。今回は「writers」とします。
- 次に、「Users」を選択して、任意のユーザー「hoge」を作成後、先ほどのグループに追加してあげます。
- 追加が終わったら、SAVEで保存します。
- これで、「writers」グループにユーザー「hoge」を追加することができました。
②settings.py
の編集
- まず、
'django.contrib.messages'
や'django.contrib.messages.middleware.MessageMiddleware'
が記載されていることを確認します(デフォルトで記載されているはず)。
app/app/settings.py
INSTALLED_APPS = [
# 他のアプリケーション
'django.contrib.messages',
]
MIDDLEWARE = [
# 他のミドルウェア
'django.contrib.messages.middleware.MessageMiddleware'
]
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
- また、
MESSAGE_STORAGE
を追記します。これにより、メッセージはセッションストレージを使用して保存されるようになります。
③urls.py
の編集
app/sample/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.restricted_for_writers, name="sample"),
]
- ルートURLに対して
views.restricted_for_writers
ビューを関連付けます。restricted_for_writers
関数は、次のviews.py
で定義します。
④views.py
の編集
app/sample/views.py
from django.shortcuts import render
from django.shortcuts import redirect
from django.contrib import messages
# Loginユーザーのみアクセス可
@login_required
def index(request):
context = list_param(None)
return render(request, 'sample/sample.html', context)
# 該当のグループに属している場合のみアクセス可
def for_groups_only(*group_names):
def decorator(view_func):
def wrapper(request, *args, **kwargs):
user_groups = request.user.groups.values_list('name', flat=True)
if any(group_name in user_groups for group_name in group_names):
return view_func(request, *args, **kwargs)
else:
messages.warning(request, 'You do not have permission')
return redirect('/')
return wrapper
return decorator
# このビューは 'writers' または 'checkers' グループに属するユーザーのみがアクセスできる
@for_groups_only('writers', 'checkers')
def restricted_for_writers(request):
return render(request, 'sample/sample.html')
-
for_groups_only
デコレータは、指定されたグループに属するユーザーのみがアクセスできるようにするためのデコレータです。 - 「writers」はDjango Authで作成したグループ名です。
⑤html
テンプレートの編集
-
base.html
テンプレート内には、メッセージフレームワークで表示されるメッセージを画面上部に表示するためのコードを記載します。 - 今回は以下のサイトよりコピーさせていただきました(Bootstrap4で表示)。
app/home/templates/pages/base.html
# 略
<body>
{% if messages %}
<ul class="messages_ul">
{% for message in messages %}
<li class="alert{% if message.tags %} alert-{{ message.tags }}{% endif %}" role="alert">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
# 略
完成。いざ検証!
- 作成したユーザー「hoge」でログインしている状態で、アクセス権限のないURLへ飛ぶボタンをクリックすると、ホーム画面に戻って以下のメッセージがページ上部に表示されることを確認できました。
- 今回は
warning
を指定していますが、他にも以下のような項目があります。
debug
info
success
warning
error
- ここのメッセージ内容や色は任意に変更ができます(詳しくはCSSなど)。Bootstrapの利用方法など、以下にも記載あるのでご参考ください。
- 今回の実装方法では、URLの直打ちでも強制的にリダイレクトができます。実際に打ち込んでみると、ちゃんとホーム画面に遷移されました。
備考
- ちなみに、
user_passes_test
デコレータを使った実装方法もあります。ただし、この場合、リダイレクト先としてlogin_url='/'
と指定しているので、アクセス権限がない場合はルートURL('/')
に飛んでしまい、メッセージの表示ができませんでした。関数の中にif not...
と記載してメッセージのコードを記載しても、リダイレクトが優先されてしまうようです。メッセージが不要な場合は以下の方が簡潔に実装できます。(このやり方で、メッセージも表示できる方法をご存知の方いましたらぜひ教えてください)
views.py
from django.shortcuts import render
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.groups.filter(name='writers').exists(), login_url='/')
def restricted_for_writers(request):
return render(request, 'sample/sample.html')
- 以上、いろいろやってみた結果、Auth機能とMessageフレームワークを使ったアクセス権限を設定する方法を実現できました。
- 今回の実装方法では、ユーザーによってはアクセスできないボタンがホーム画面に出る状態です。ユーザーごとにボタンの表示が変わる方がセキュリティ的には好ましいと思うので、別途その実装もしてみようと思います。
- また、「アクセス権限がありません」というページを準備して、「ホーム画面に戻る」みたいなボタンを下部に設置するのも親切かもしれませんね。