Django REST Framework Social-OAuth2社交登录后重定向401未授权错误
嘿,我之前在使用django-rest-framework-social-oauth2时也踩过这个坑,咱们先把问题根源理清楚,再给你几个可行的解决办法。
问题到底出在哪?
你遇到的401错误核心很明确:社交登录成功后,系统确实生成了有效的OAuth2 Token,但重定向到/users/ldata/的GET请求根本没带任何认证凭证。
你的DRF配置里指定了必须用OAuth2Authentication或者SocialAuthentication来验证身份,而浏览器的重定向请求不会自动把Token塞进请求头或者参数里,后端自然认不出你是谁,直接返回“未提供认证凭证”的错误。
另外补充下:你的/users/ldata/应该是个DRF的API视图(不是普通Django模板视图),所以它会严格执行REST_FRAMEWORK的认证规则,没凭证就直接拒接。
解决办法分场景选
根据你的项目是传统服务还是前后端分离,我整理了3种方案:
方案1:重定向时把Token塞到URL参数里(适合传统服务)
我们可以自定义社交登录的完成逻辑,在登录成功后拿到Token,再拼到重定向的URL里,然后让API视图识别这个参数:
- 先写个自定义的登录完成视图:
from django.shortcuts import redirect from social_django.views import complete from oauth2_provider.models import AccessToken from django.contrib.auth import get_user_model User = get_user_model() def custom_social_complete(request, backend, *args, **kwargs): # 先跑默认的社交登录流程 response = complete(request, backend, *args, **kwargs) # 拿到当前登录的用户 user = request.user if user.is_authenticated: # 获取用户最新的access_token access_token = AccessToken.objects.filter(user=user).latest('created') # 拼接带token的重定向地址 redirect_url = f"/users/ldata/?token={access_token.token}" return redirect(redirect_url) return response
- 替换urls.py里默认的social-auth完成路由:
from django.urls import path from .views import custom_social_complete urlpatterns = [ # 把原来的complete路由换成咱们自定义的 path('complete/<str:backend>/', custom_social_complete, name='social_complete'), # 其他路由照常写... ]
- 给你的
/users/ldata/视图加个逻辑,识别URL里的token并放到请求头:
from rest_framework.views import APIView from rest_framework.response import Response from oauth2_provider.contrib.rest_framework import OAuth2Authentication from rest_framework.permissions import IsAuthenticated class UserLdataView(APIView): authentication_classes = [OAuth2Authentication] permission_classes = [IsAuthenticated] def get(self, request): # 这里写你的业务逻辑,比如返回用户数据 return Response({"user_info": "你的用户数据"}) def initial(self, request, *args, **kwargs): # 从URL参数里拿token,塞进Authorization请求头 token = request.query_params.get('token') if token: request.META['HTTP_AUTHORIZATION'] = f'Bearer {token}' # 调用父类的初始化逻辑 super().initial(request, *args, **kwargs)
方案2:前后端分离场景?让前端拿Token自己请求
如果你的项目是前后端分离的,别用重定向这种老办法了,直接让社交登录流程返回Token给前端,再由前端带着Token去请求API:
- 先在settings.py里加一行,让social-auth返回JSON格式的响应:
SOCIAL_AUTH_JSONFIELD_ENABLED = True
- 自定义登录完成视图,直接返回Token的JSON:
from django.http import JsonResponse from social_django.views import complete from oauth2_provider.models import AccessToken def custom_social_complete(request, backend, *args, **kwargs): response = complete(request, backend, *args, **kwargs) user = request.user if user.is_authenticated: access_token = AccessToken.objects.filter(user=user).latest('created') return JsonResponse({ 'access_token': access_token.token, 'refresh_token': access_token.refresh_token.token, 'expires_in': access_token.expires_in }) return response
- 前端拿到Token后,请求
/users/ldata/时,在请求头里加上Authorization: Bearer <你的token>就行。
方案3:允许用Session认证(适合混合场景,不推荐纯API)
如果你的项目既有Django模板页面又有DRF API,可以给REST_FRAMEWORK加个Session认证,这样登录后的Session就能用来识别API请求:
修改settings.py里的REST_FRAMEWORK配置:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'rest_framework_social_oauth2.authentication.SocialAuthentication', 'rest_framework.authentication.SessionAuthentication', # 加这一行 ), # 其他配置不变... }
这样用户社交登录后(Session已经建立),重定向到API时,DRF会通过Session认出用户,就不会报401了。不过纯API服务不建议用这个方法,还是Token认证更规范。
最后检查个小细节
你贴的settings.py里rest_framework_social_oaut...没写完,记得确认rest_framework_social_oauth2.authentication.SocialAuthentication已经正确加到DEFAULT_AUTHENTICATION_CLASSES里了哦。
内容的提问来源于stack exchange,提问作者msd

