You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Azure托管的Django项目中从零实现Microsoft登录与权限控制

从零实现Django对接Microsoft登录及权限控制

一、对接Microsoft登录(基于OAuth2.0授权码流程)

1. 前置准备

在Azure AD中注册应用,获取以下信息:

  • client_id:应用ID
  • client_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:43:16