我正在遵循这个 视频教程 并尝试使用
DRF
、djoser
和 React
来实现 Google 社交身份验证。
导致错误的步骤:
发送
GET
请求:
http://localhost:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:8000
响应看起来像这样(我稍微修改了响应,因为我不确定这个网址是否可以安全共享)
{
"authorization_url": "https://accounts.google.com/o/oauth2/auth?client_id=836198290956-fe0ilujf6e23l882oumgkufi8qm6fg3m.apps.googleusercontent.com&redirect_uri=http://localhost:8000&state=eNwMFCmEplYgbUTTP9nnrQ6MduAPxzDY&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile+openid+openid+email+profile"
}
sign in
页面。然后我选择我的帐户,然后按继续。我现在通过此网址重定向到 localhost:8000
http://localhost:8000/?state=eNwMFCmEplYgbUTTP9nnrQ6MduAPxzDY&code=4%2F0AcvDMrB6f3ZQuTD563Vxriu2n0VHmLEOHnDRqC6jD5BRm068jj2tyExxfZZJDFLAtcwYLg&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&authuser=0&hd=circle.help&prompt=consent
我的问题出现在这里。
localhost:8000/auth/o/google-oauth2/?state=eNwMFCmEplYgbUTTP9nnrQ6MduAPxzDY&code=4%2F0AcvDMrB6f3ZQuTD563Vxriu2n0VHmLEOHnDRqC6jD5BRm068jj2tyExxfZZJDFLAtcwYLg
但作为回应,我收到了这个(这是我的问题):
{
"non_field_errors": [
"Invalid state has been provided."
]
}
我尝试调试一下,发现原因就在这个模块:
# venv/lib/python3.11/site-packages/social_core/backends/oauth.py
class OAuthAuth(BaseAuth):
...
# Other methods
def validate_state(self):
"""Validate state value. Raises exception on error, returns state
value if valid."""
if not self.STATE_PARAMETER and not self.REDIRECT_STATE:
return None
state = self.get_session_state()
request_state = self.get_request_state()
if not request_state:
raise AuthMissingParameter(self, "state")
elif not state:
raise AuthStateMissing(self, "state")
elif not constant_time_compare(request_state, state):
raise AuthStateForbidden(self)
else:
return state
...
# Other methods
就我而言,在 OAuthAuth.validate_state() 方法中, state 变量与 request_state 变量不同,而 request_state (来自 OAuthAuth.validate_state())与状态(来自 url)相同
在上面两个 url 中,但是 state (来自 OAuthAuth.validate_state())完全不同。我不知道它从哪里来,为什么
self.get_request_state()
返回与 url 中不同的状态?也许我做错了什么,我应该在 Postman 中传递一些 cookie?
这是我安装的软件包的列表:
asgiref==3.8.1
certifi==2024.7.4
cffi==1.16.0
charset-normalizer==3.3.2
cryptography==42.0.8
defusedxml==0.8.0rc2
Django==5.0.7
django-filter==24.2
django-templated-mail==1.1.1
djangorestframework==3.15.2
djangorestframework-simplejwt==5.3.1
djoser==2.2.3
idna==3.7
Markdown==3.6
oauthlib==3.2.2
psycopg==3.2.1
psycopg2-binary==2.9.9
pycparser==2.22
PyJWT==2.8.0
python3-openid==3.2.0
requests==2.32.3
requests-oauthlib==2.0.0
social-auth-app-django==5.4.2
social-auth-core==4.5.4
sqlparse==0.5.1
typing_extensions==4.12.2
urllib3==2.2.2
这是我的设置.py
SECRET_KEY = "django_secret_key"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# Third party apps
"rest_framework",
"djoser",
"social_django",
"rest_framework_simplejwt",
"rest_framework_simplejwt.token_blacklist", # more smooth with migration
# My apps
"accounts"
]
MIDDLEWARE = [
"social_django.middleware.SocialAuthExceptionMiddleware",
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "djSocAuth.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
# "DIRS": [BASE_DIR / 'templates'], # os.path.join(BASE_DIR, "build")
"DIRS": [os.path.join(BASE_DIR, "build")], # os.path.join(BASE_DIR, "build")
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"social_django.context_processors.backends",
"social_django.context_processors.login_redirect"
],
},
},
]
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": {
"rest_framework.permissions.IsAuthenticated"
},
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
AUTHENTICATION_BACKENDS = (
"social_core.backends.google.GoogleOAuth2", # Enable using google OAuth 2
"django.contrib.auth.backends.ModelBackend", # Enable logining via email and password
)
SIMPLE_JWT = {
'AUTH_HEADER_TYPES': ('JWT',),
"ACCESS_TOKEN_LIFETIME": timedelta(days=10000),
"REFRESH_TOKEN_LIFETIME": timedelta(days=10000),
"AUTH_TOKEN_CLASSES": (
"rest_framework_simplejwt.tokens.AccessToken",
)
}
from djoser.social.token.jwt import TokenStrategy
DJOSER = {
"LOGIN_FIELD": "email",
"USER_CREATE_PASSWORD_RETYPE": True, # confirm password field
"USERNAME_CHANGED_EMAIL_CONFIRMATION": True, # whenever username is changed - confirmation email is sent
"PASSWORD_CHANGED_EMAIL_CONFIRMATION": True,
"SET_USERNAME_RETYPE": True,
"SET_PASSWORD_RETYPE": True,
"PASSWORD_RESET_CONFIRM_URL": "password/reset/confirm/{uid}/{token}",
"USERNAME_RESET_CONFIRM_URL": "email/reset/confirm/{uid}/{token}",
"ACTIVATION_URL": "activate/{uid}/{token}", # this should be on frontend, --> auth/users/activation/
"SEND_ACTIVATION_EMAIL": True,
"SOCIAL_AUTH_TOKEN_STRATEGY": "djoser.social.token.jwt.TokenStrategy",
"SOCIAL_AUTH_ALLOWED_REDIRECT_URIS": [
"http://localhost:8000"
],
"SERIALIZERS": {
"user_create": "accounts.serializers.UserCreateSerializer",
"user": "accounts.serializers.UserCreateSerializer",
"current_user": "accounts.serializers.UserCreateSerializer",
"user_delete": "djoser.serializers.UserDeleteSerializer",
}
}
# Google config
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = "my_key"
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = "my_secret"
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
"https://www.googleapis.com/auth/userinfo.email", # retrieve email
"https://www.googleapis.com/auth/userinfo.profile",
"openid"
] # when people go to sign up and sing in, they are going to retrieve some data from their accounts
SOCIAL_AUTH_GOOGLE_OAUTH2_EXTRA_DATA = ["first_name", "last_name"]
AUTH_USER_MODEL = "accounts.UserAccount"
...
我发现了一堆类似的问题,但没有完全解释这个错误的原因是什么。
您遇到的问题是,当您进行重定向时,状态不会持久。状态保存在
session
键下的 f'_state_{self.name}_{state}'
中。 self.name
值并不重要。但 state
是。在验证您的凭据后进行重定向时,state
值会作为请求参数从 Google 返回。该值与会话或缓存中的值进行比较。
所以基本上,当您调用后端时,您应该保留
session
。如果您使用浏览器,最好使用隐身模式。如果您还想使用邮递员,请在创建请求之前将会话复制到请求。