如何在Azure托管的Django项目中从零实现Microsoft登录与权限控制
从零实现Django对接Microsoft登录及权限控制
一、对接Microsoft登录(基于OAuth2.0授权码流程)
1. 前置准备
在Azure AD中注册应用,获取以下信息:
client_id:应用IDclient_secret:应用密钥tenant_id:租户ID(如果是组织内登录用租户ID,通用登录用common)- 配置重定向URL:比如
https://your-domain.com/microsoft-callback/,必须和Azure AD中注册的一致
2. 实现登录跳转视图
构造Microsoft授权页面的URL,引导用户跳转至Microsoft登录页:
from django.shortcuts import redirect from urllib.parse import urlencode import secrets def microsoft_login(request): # 生成随机state值防CSRF state = secrets.token_urlsafe(16) request.session['oauth_state'] = state params = { 'client_id': 'YOUR_AZURE_CLIENT_ID', 'redirect_uri': 'https://your-domain.com/microsoft-callback/', 'response_type': 'code', 'scope': 'openid email profile', 'state': state, } auth_url = f'https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/authorize?{urlencode(params)}' return redirect(auth_url)
3. 实现回调视图
接收授权码,交换获取用户信息,并关联Django用户系统:
import requests from django.contrib.auth import login, get_user_model from django.shortcuts import redirect from django.http import HttpResponseBadRequest User = get_user_model() def microsoft_callback(request): # 验证state防CSRF state = request.GET.get('state') if state != request.session.get('oauth_state'): return HttpResponseBadRequest('无效的请求状态') code = request.GET.get('code') if not code: return redirect('microsoft-login') # 用授权码交换Token token_url = f'https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token' payload = { 'client_id': 'YOUR_AZURE_CLIENT_ID', 'client_secret': 'YOUR_AZURE_CLIENT_SECRET', 'code': code, 'redirect_uri': 'https://your-domain.com/microsoft-callback/', 'grant_type': 'authorization_code' } response = requests.post(token_url, data=payload) token_data = response.json() # 调用Microsoft Graph获取用户信息 graph_url = 'https://graph.microsoft.com/v1.0/me' headers = {'Authorization': f'Bearer {token_data["access_token"]}'} user_info = requests.get(graph_url, headers=headers).json() # 提取唯一标识(优先用邮箱) email = user_info.get('mail') or user_info.get('userPrincipalName') if not email: return HttpResponseBadRequest('无法获取用户邮箱信息') # 关联Django用户:存在则直接登录,不存在则创建 user, created = User.objects.get_or_create( email=email, defaults={ 'username': email.split('@')[0], 'first_name': user_info.get('givenName', ''), 'last_name': user_info.get('surname', '') } ) # 完成Django登录 login(request, user) return redirect('home') # 跳转到项目首页
二、阻止未登录用户访问视图
方法1:全局中间件控制
自定义中间件,拦截未登录用户的请求(排除登录、回调等白名单URL):
# myapp/middleware.py from django.shortcuts import redirect from django.conf import settings class LoginRequiredMiddleware: def __init__(self, get_response): self.get_response = get_response # 无需登录的URL白名单 self.exempt_paths = [ settings.LOGIN_URL, '/microsoft-login/', '/microsoft-callback/', # 可添加静态资源路径等 ] def __call__(self, request): if not request.user.is_authenticated: path = request.path_info if not any(path.startswith(p) for p in self.exempt_paths): return redirect(settings.LOGIN_URL) response = self.get_response(request) return response
在settings.py的MIDDLEWARE列表中添加该中间件:
MIDDLEWARE = [ # 其他中间件... 'myapp.middleware.LoginRequiredMiddleware', ]
方法2:视图装饰器
对单个视图使用Django自带的@login_required装饰器:
from django.contrib.auth.decorators import login_required @login_required def dashboard(request): # 仅登录用户可访问的视图逻辑 ...
三、设置权限组与权限控制
利用Django自带的Group和Permission模型实现分级权限:
1. 创建权限组
可通过Django Admin后台创建,或用代码批量创建:
from django.contrib.auth.models import Group, Permission from myapp.models import Post # 示例模型 # 创建分组 admin_group, _ = Group.objects.get_or_create(name='管理员') editor_group, _ = Group.objects.get_or_create(name='编辑') viewer_group, _ = Group.objects.get_or_create(name='查看者') # 为分组分配权限(以Post模型为例) post_permissions = Permission.objects.filter(content_type__model='post') # 管理员拥有所有Post权限 admin_group.permissions.add(*post_permissions) # 编辑拥有新增、修改权限 editor_perms = post_permissions.filter(codename__in=['add_post', 'change_post']) editor_group.permissions.add(*editor_perms) # 查看者仅拥有查看权限 viewer_perms = post_permissions.filter(codename='view_post') viewer_group.permissions.add(*viewer_perms)
2. 用户分组分配
可在登录回调时根据用户属性(如邮箱域名)自动分配,或通过Admin后台手动设置:
# 在microsoft_callback视图中添加分组分配逻辑 if email.endswith('your-company.com'): user.groups.add(admin_group) elif email.endswith('editor.your-company.com'): user.groups.add(editor_group) else: user.groups.add(viewer_group) user.save()
3. 视图权限校验
使用@user_passes_test装饰器或直接在视图内校验:
from django.contrib.auth.decorators import user_passes_test from django.http import HttpResponseForbidden # 校验是否为管理员 def is_admin(user): return user.groups.filter(name='管理员').exists() @user_passes_test(is_admin) def admin_panel(request): # 仅管理员可访问 ... # 校验是否为编辑或管理员 def edit_post(request, pk): if not request.user.groups.filter(name__in=['管理员', '编辑']).exists(): return HttpResponseForbidden('无权限执行此操作') # 编辑逻辑 ...
内容的提问来源于stack exchange,提问作者JohnFahey
相关产品推荐
相关产品推荐

