如何基于AngularJS前端与DRF后端实现带JWT返回的社交登录功能
嘿,我来帮你一步步实现这个AngularJS + DRF的社交登录方案,完全贴合你的需求,咱们分前端和后端两部分来拆解:
整体流程对齐
先确认下咱们的核心流程,和你描述的完全一致:
- 用户点击FB/Google登录按钮,完成第三方授权,拿到
access_token - AngularJS把
access_token和登录类型(FB/Google)POST给DRF后端 - 后端验证第三方
access_token的合法性,获取用户的名、姓、邮箱 - 后端查找是否已有关联该第三方账号的用户,没有则创建新用户
- 后端生成JWT令牌,以HttpOnly Cookie形式返回给前端
- 后续所有API请求,浏览器自动带上JWT Cookie,无需再次走第三方登录
后端DRF实现步骤
1. 安装依赖包
先装必要的工具库,在你的Django项目根目录执行:
pip install djangorestframework djangorestframework-simplejwt requests
djangorestframework-simplejwt:用来生成和验证JWT令牌requests:用来调用FB/Google的token验证API
2. 配置SimpleJWT和DRF
在settings.py里添加以下配置:
# DRF配置 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), } # SimpleJWT配置,重点设置Cookie相关参数 from datetime import timedelta SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(hours=24), 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), # 把令牌放在Cookie里返回 'AUTH_COOKIE': 'access_token', 'AUTH_COOKIE_REFRESH': 'refresh_token', 'AUTH_COOKIE_SECURE': True, # 生产环境设为True,必须HTTPS 'AUTH_COOKIE_HTTPONLY': True, # 关键!防止XSS攻击,JS无法读取 'AUTH_COOKIE_SAMESITE': 'Lax', # 防止CSRF,根据需求选Strict/Lax } # 别忘了把rest_framework和rest_framework_simplejwt加入INSTALLED_APPS INSTALLED_APPS = [ # ... 其他已有的app 'rest_framework', 'rest_framework_simplejwt', ]
3. 创建社交登录视图
在你的app里新建views.py的视图函数,处理前端传来的access_token:
from django.http import JsonResponse from django.contrib.auth import get_user_model from rest_framework.views import APIView from rest_framework import status import requests User = get_user_model() class SocialLoginView(APIView): def post(self, request): provider = request.data.get('provider') # 'facebook' 或 'google' access_token = request.data.get('access_token') if not provider or not access_token: return JsonResponse({'error': '缺少必要参数'}, status=status.HTTP_400_BAD_REQUEST) # 1. 调用第三方API验证token并获取用户信息 user_info = None if provider == 'facebook': # FB的验证接口,获取用户的名、姓、邮箱 fb_url = f"https://graph.facebook.com/me?access_token={access_token}&fields=first_name,last_name,email,id" response = requests.get(fb_url) if response.status_code != 200: return JsonResponse({'error': 'FB令牌无效'}, status=status.HTTP_400_BAD_REQUEST) user_info = response.json() elif provider == 'google': # Google的验证接口,注意如果是id_token的话用tokeninfo接口,access_token用userinfo接口 google_url = f"https://www.googleapis.com/oauth2/v3/userinfo?access_token={access_token}" response = requests.get(google_url) if response.status_code != 200: return JsonResponse({'error': 'Google令牌无效'}, status=status.HTTP_400_BAD_REQUEST) user_info = response.json() else: return JsonResponse({'error': '不支持的登录方式'}, status=status.HTTP_400_BAD_REQUEST) # 2. 提取用户信息 first_name = user_info.get('first_name') or user_info.get('given_name') last_name = user_info.get('last_name') or user_info.get('family_name') email = user_info.get('email') provider_uid = user_info.get('id') if not email: return JsonResponse({'error': '第三方账号未绑定邮箱'}, status=status.HTTP_400_BAD_REQUEST) # 3. 查找或创建用户,这里建议用关联表记录第三方账号(避免重复) # 先查是否已有该第三方账号关联的用户 from django.db.models import Q # 假设你有一个SocialAccount模型,字段:user(外键)、provider(字符串)、uid(字符串) try: social_account = SocialAccount.objects.get(provider=provider, uid=provider_uid) user = social_account.user except SocialAccount.DoesNotExist: # 没有的话,先查邮箱是否已存在用户 try: user = User.objects.get(email=email) # 关联已有用户和第三方账号 SocialAccount.objects.create(user=user, provider=provider, uid=provider_uid) except User.DoesNotExist: # 创建新用户 user = User.objects.create( first_name=first_name, last_name=last_name, email=email, username=email.split('@')[0] # 用邮箱前缀当用户名,或者生成随机字符串 ) user.set_unusable_password() # 社交登录用户不需要密码 user.save() # 关联第三方账号 SocialAccount.objects.create(user=user, provider=provider, uid=provider_uid) # 4. 生成JWT令牌并以Cookie返回 from rest_framework_simplejwt.tokens import RefreshToken refresh = RefreshToken.for_user(user) response = JsonResponse({'message': '登录成功', 'user': {'email': user.email, 'name': f"{user.first_name} {user.last_name}"}}) # 设置Access Token Cookie response.set_cookie( key='access_token', value=str(refresh.access_token), expires=SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'], secure=SIMPLE_JWT['AUTH_COOKIE_SECURE'], httponly=SIMPLE_JWT['AUTH_COOKIE_HTTPONLY'], samesite=SIMPLE_JWT['AUTH_COOKIE_SAMESITE'] ) # 可选:设置Refresh Token Cookie,用来刷新令牌 response.set_cookie( key='refresh_token', value=str(refresh), expires=SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'], secure=SIMPLE_JWT['AUTH_COOKIE_SECURE'], httponly=SIMPLE_JWT['AUTH_COOKIE_HTTPONLY'], samesite=SIMPLE_JWT['AUTH_COOKIE_SAMESITE'] ) return response
注意:需要创建
SocialAccount模型,在app的models.py里:from django.db import models from django.contrib.auth import get_user_model User = get_user_model() class SocialAccount(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='social_accounts') provider = models.CharField(max_length=20) # 'facebook', 'google' uid = models.CharField(max_length=100, unique=True) # 第三方账号的唯一ID class Meta: unique_together = ('provider', 'uid')然后执行
python manage.py makemigrations和python manage.py migrate创建表。
4. 配置URL路由
在项目的urls.py里添加:
from django.urls import path from .views import SocialLoginView urlpatterns = [ # ... 其他路由 path('api/social-login/', SocialLoginView.as_view(), name='social-login'), ]
前端AngularJS实现步骤
1. 集成第三方登录SDK
在你的index.html里引入FB和Google的SDK:
<!-- Facebook SDK --> <script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v18.0&appId=你的FB_APP_ID&autoLogAppEvents=1" nonce="随机字符串"></script> <!-- Google Login SDK --> <script src="https://accounts.google.com/gsi/client" async defer></script>
记得替换
你的FB_APP_ID为你在FB开发者平台创建的应用ID,Google的话需要在Google Cloud控制台创建OAuth 2.0客户端ID。
2. 创建登录控制器和按钮
在AngularJS的controller里写登录逻辑:
angular.module('yourApp', []) .controller('LoginController', function($scope, $http) { // 初始化FB SDK window.fbAsyncInit = function() { FB.init({ appId : '你的FB_APP_ID', cookie : true, xfbml : true, version : 'v18.0' }); }; // Facebook登录 $scope.facebookLogin = function() { FB.login(function(response) { if (response.status === 'connected') { // 拿到FB的access_token const accessToken = response.authResponse.accessToken; // 发送请求到后端 $http.post('/api/social-login/', { provider: 'facebook', access_token: accessToken }).then(function(res) { console.log('登录成功', res.data); // 这里可以跳转到首页或者更新用户状态 window.location.href = '/dashboard'; }).catch(function(err) { console.error('登录失败', err.data); alert(err.data.error || '登录出错,请重试'); }); } else { alert('用户未授权'); } }, {scope: 'email public_profile'}); // 请求必要的权限 }; // Google登录 $scope.googleLogin = function() { // 初始化Google Auth google.accounts.id.initialize({ client_id: '你的GOOGLE_CLIENT_ID', callback: handleGoogleResponse }); // 弹出登录框 google.accounts.id.prompt(); function handleGoogleResponse(response) { // 这里拿到的是id_token,如果你需要access_token的话,用gapi.auth2方式 const auth2 = gapi.auth2.getAuthInstance(); auth2.signIn().then(function(googleUser) { const accessToken = googleUser.getAuthResponse().access_token; // 发送请求到后端 $http.post('/api/social-login/', { provider: 'google', access_token: accessToken }).then(function(res) { console.log('登录成功', res.data); window.location.href = '/dashboard'; }).catch(function(err) { console.error('登录失败', err.data); alert(err.data.error || '登录出错,请重试'); }); }); } }; });
然后在HTML里添加登录按钮:
<div ng-controller="LoginController"> <button ng-click="facebookLogin()">用Facebook登录</button> <button ng-click="googleLogin()">用Google登录</button> </div>
3. 后续API请求处理
因为JWT是存在HttpOnly Cookie里的,浏览器会自动在后续的API请求中带上这个Cookie,所以你不需要手动处理JWT,直接用$http发送请求即可:
$http.get('/api/user-profile/').then(function(res) { console.log('当前用户信息', res.data); });
后端的视图只要加上permission_classes = [IsAuthenticated]就能验证JWT的有效性。
注意事项
- 安全性:生产环境一定要开启
AUTH_COOKIE_SECURE=True,确保Cookie只在HTTPS下传输;HttpOnly属性必须开启,防止XSS攻击窃取令牌。 - 邮箱冲突:如果用户之前用邮箱注册过,现在用社交登录,后端要处理这种情况,比如提示用户关联已有账号,或者自动关联(根据你的业务需求)。
- 令牌刷新:可以实现一个刷新令牌的接口,当access_token过期时,用refresh_token获取新的access_token。
- 错误处理:前端要处理后端返回的各种错误,比如令牌无效、第三方账号无邮箱等,给用户友好的提示。
内容的提问来源于stack exchange,提问作者DEEPAK KUMAR
相关产品推荐
相关产品推荐

