我使用Django和Allauth + REST-Auth进行SPA社交登录并成功设置了Facebook,VK和Google授权,但在添加Twitter时遇到了问题。它最终以{"代码":89,"消息":"无效或过期的令牌。"} 看起来我错过了一些东西&标准登录与Twitter的工作原理
以下是我的尝试:
首先,我按照doc:
中的描述设置了Twitter登录端点class TwitterLogin(SocialLoginView):
serializer_class = TwitterLoginSerializer
adapter_class = CustomTwitterOAuthAdapter
它具有post方法,期待access_token和token_secret 因此创建了重定向视图以接收来自twitter的重定向,完成登录并通过模板渲染(使用几行JS线)将内部django令牌设置为浏览器localStorage:
class TwitterReceiveView(APIView):
def get(self, request, *args, **kwargs):
access_token = request.query_params.get('oauth_token')
token_secret = request.query_params.get('oauth_verifier')
params = {'access_token': access_token,
'token_secret': token_secret}
try:
result = requests.post(settings.DOMAIN + reverse('tw_login'), data=params).text
result = json.loads(result)
except (requests.HTTPError, json.decoder.JSONDecodeError):
result = {}
access_token = result.get('access_token')
context = {'access_token': access_token}
return render(request, 'account/local_storage_setter.html',
context, content_type='text/html')
不得不提的是我尝试了两种方法来启动进程(获取初始令牌) 1.使用标准allauth网址http://0.0.0.0:8080/accounts/twitter/login 2.创建了另一个可以在SPA中使用的视图(使用lib python oauth2):
class TwitterGetToken(APIView):
def get(self, request, *args, **kwargs):
request_token_url = 'https://api.twitter.com/oauth/request_token'
authorize_url = 'https://api.twitter.com/oauth/authorize'
app = SocialApp.objects.filter(name='Twitter').first()
if app and app.client_id and app.secret:
consumer = oauth.Consumer(app.client_id, app.secret)
client = oauth.Client(consumer)
resp, content = client.request(request_token_url, "GET")
if resp['status'] != '200':
raise Exception("Invalid response {}".format(resp['status']))
request_token = dict(urllib.parse.parse_qsl(content.decode("utf-8")))
twitter_authorize_url = "{0}?oauth_token={1}"\
.format(authorize_url, request_token['oauth_token'])
return redirect(twitter_authorize_url)
raise Exception("Twitter app is not set up")
我甚至尝试为FacebookLoginView编写get方法并直接将twitter回调传递给它
class TwitterLogin(SocialLoginView):
serializer_class = TwitterLoginSerializer
adapter_class = TwitterOAuthAdapter
def get(self, request, *args, **kwargs):
data = {
'access_token': request.query_params.get('oauth_token'),
'token_secret': request.query_params.get('oauth_verifier')
}
self.request = request
self.serializer = self.get_serializer(data=data,
context={'request': request})
self.serializer.is_valid(raise_exception=True)
self.login()
return self.get_response()
所有方法都让我提到了错误。请你在我的案例中提出建议。提前谢谢!
更新 以下是它对我有用的方式:
import json
import requests
import urllib.parse
import oauth2 as oauth
from requests_oauthlib import OAuth1Session
from django.urls import reverse
from django.conf import settings
from django.shortcuts import redirect, render
from rest_framework.views import APIView
from allauth.socialaccount.models import SocialApp
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter, TwitterAPI
from rest_auth.social_serializers import TwitterLoginSerializer
from rest_auth.registration.views import SocialLoginView
class TwitterGetToken(APIView):
'''
Initiates Twitter login process
Requests initial token and redirects user to Twitter
'''
def get(self, request, *args, **kwargs):
request_token_url = 'https://api.twitter.com/oauth/request_token'
authorize_url = 'https://api.twitter.com/oauth/authorize'
app = SocialApp.objects.filter(name='Twitter').first()
if app and app.client_id and app.secret:
consumer = oauth.Consumer(app.client_id, app.secret)
client = oauth.Client(consumer)
resp, content = client.request(request_token_url, "GET")
if resp['status'] != '200':
raise Exception("Invalid response {}".format(resp['status']))
request_token = dict(urllib.parse.parse_qsl(content.decode("utf-8")))
twitter_authorize_url = "{0}?oauth_token={1}"\
.format(authorize_url, request_token['oauth_token'])
return redirect(twitter_authorize_url)
raise Exception("Twitter app is not set up")
class TwitterLogin(SocialLoginView):
'''
Takes the final twitter access token, secret
Returns inner django Token
'''
serializer_class = TwitterLoginSerializer
adapter_class = TwitterOAuthAdapter
class TwitterReceiveView(APIView):
'''
Receives Twitter redirect,
Requests access token
Uses TwitterLogin to logn and get django Token
Renders template with JS code which sets django Token to localStorage and redirects to SPA login page
'''
def get(self, request, *args, **kwargs):
access_token_url = 'https://api.twitter.com/oauth/access_token'
callback_uri = settings.DOMAIN + '/accounts/twitter/login/callback/'
app = SocialApp.objects.filter(name='Twitter').first()
client_key = app.client_id
client_secret = app.secret
oauth_session = OAuth1Session(client_key,
client_secret=client_secret,
callback_uri=callback_uri)
redirect_response = request.get_full_path()
oauth_session.parse_authorization_response(redirect_response)
token = oauth_session.fetch_access_token(access_token_url)
params = {'access_token': token['oauth_token'],
'token_secret': token['oauth_token_secret']}
try:
result = requests.post(settings.DOMAIN + reverse('tw_login'),
data=params).text
result = json.loads(result)
except (requests.HTTPError, json.decoder.JSONDecodeError):
result = {}
access_token = result.get('access_token')
context = {'access_token': access_token,
'domain': settings.DOMAIN}
return render(request, 'account/local_storage_setter.html',
context, content_type='text/html')
答案 0 :(得分:0)
很棒的代码,谢谢您的发布!
但是我想补充一点,可以直接从前端完成用户身份验证,并且鉴于您正在编写SPA,这样做似乎合乎逻辑,而不是在后端重定向您的身份(哪种方式破坏了RESTful的概念)以进行身份验证,然后发送响应。
我使用了以下基于vue-authenticate的JS helper类。要弹出并从回调URL获取信息
export default class OAuthPopup {
constructor(url, name, redirectURI) {
this.popup = null
this.url = url
this.name = name
this.redirectURI = redirectURI
}
open() {
try {
this.popup = window.open(this.url, this.name)
if (this.popup && this.popup.focus) {
this.popup.focus()
}
return this.pooling()
} catch(e) {
console.log(e)
}
}
pooling() {
return new Promise((resolve, reject) => {
let poolingInterval = setInterval(() => {
if (!this.popup || this.popup.closed || this.popup.closed === undefined) {
clearInterval(poolingInterval)
poolingInterval = null
reject(new Error('Auth popup window closed'))
}
try {
var popupLocation = this.popup.location.origin + this.popup.location.pathname
if (popupLocation == this.redirectURI) {
if (this.popup.location.search || this.popup.location.hash ) {
const urlParams = new URLSearchParams(this.popup.location.search);
var params = {
oauth_token: urlParams.get('oauth_token'),
oauth_verifier: urlParams.get('oauth_verifier'),
url: this.popup.location.href
}
if (params.error) {
reject(new Error(params.error));
} else {
resolve(params);
}
} else {
reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.'))
}
clearInterval(poolingInterval)
poolingInterval = null
this.popup.close()
}
} catch(e) {
// Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame.
}
}, 250)
})
}
}
但是,我遵循的方法与您类似:
其他所有内容都放置到位,并向用户返回访问密钥。
无论如何, 谢谢,我弄乱了js和python中的库负载,但这只是做事的最佳方法