【Web】Django OAuth invalid_grant error

本文详细介绍了如何解决Django OAuth Toolkit中Grant过期问题,通过更改过期时间来延长其有效期。同时,文章还指导了在Django环境中实现QQ登录的方法,并提供了具体步骤与配置细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

This can happen if the code is expired. They expire pretty quickly by default. You can look up your code in the Django admin under Grants and change the expiration date to far in the future.

原因:

Django OAuth Toolkit  默认的Grant过期时间非常短暂。

 

Django使用django-oauth-toolkit无法使用json格式进行Oauth2认证:http://www.tuicool.com/articles/7ryYfuq

在django中实现QQ登录:http://www.oschina.net/question/565065_69148

放置“QQ登录”按钮:_OAuth2.0:http://wiki.open.qq.com/wiki/website/%E6%94%BE%E7%BD%AE%E2%80%9CQQ%E7%99%BB%E5%BD%95%E2%80%9D%E6%8C%89%E9%92%AE_OAuth2.0

开发攻略_Server-side :http://wiki.open.qq.com/wiki/website/%E5%BC%80%E5%8F%91%E6%94%BB%E7%95%A5_Server-side

 

 

参考资料:

http://stackoverflow.com/questions/27056272/django-oauth-toolkit-invalid-grant-error

 

转载于:https://www.cnblogs.com/junneyang/p/5502090.html

import os import time import cv2 import numpy as np import base64 import json import threading from .page_4_1_auto_set import image_detect from django.conf import settings from django.views.decorators.csrf import csrf_exempt from django.shortcuts import render, redirect from django.contrib import messages from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.tokens import default_token_generator from django.utils.encoding import force_bytes, force_str from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.core.mail import send_mail from django.contrib.auth.models import User from .models import UserInfo from ultralytics import YOLO from django.forms import Form, FileField from django.contrib.auth.forms import AuthenticationForm from django.http import JsonResponse, StreamingHttpResponse class VideoUploadForm(Form): video = FileField() def wechat_login(request): code = request.GET.get('code') if code: # 获取 access_token url = f'https://api.weixin.qq.com/sns/oauth2/access_token?appid={settings.WECHAT_APPID}&secret={settings.WECHAT_SECRET}&code={code}&grant_type=authorization_code' import requests response = requests.get(url) data = response.json() if 'access_token' in data: access_token = data['access_token'] openid = data['openid'] # 获取用户信息 user_info_url = f'https://api.weixin.qq.com/sns/userinfo?access_token={access_token}&openid={openid}' user_info_response = requests.get(user_info_url) user_info = user_info_response.json() # 处理用户信息,例如创建或登录用户 # ... return redirect('home') else: # 重定向到微信授权页面 authorize_url = f'https://open.weixin.qq.com/connect/oauth2/authorize?appid={settings.WECHAT_APPID}&redirect_uri={settings.WECHAT_REDIRECT_URI}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect' return redirect(authorize_url) def register(request): if request.method == 'POST': username = request.POST.get('username') email = request.POST.get('email') password1 = request.POST.get('password1') password2 = request.POST.get('password2') # 检查用户名是否已存在 if UserInfo.objects.filter(user__username=username).exists(): messages.error(request, '用户名已注册,请选择其他用户名!') return render(request, 'register.html', { 'username': username, 'email': email, 'error': 'username_exists' }, status=400) # 检查用户名长度 if len(username) < 3: messages.error(request, '用户名至少3个字符!') return render(request, 'register.html', { 'username': username, 'email': email, 'error': 'username_short' }, status=400) # 检查邮箱是否已存在 if UserInfo.objects.filter(email=email).exists(): messages.error(request, '邮箱已注册,请使用其他邮箱!') return render(request, 'register.html', { 'username': username, 'email': email, 'error': 'email_exists' }, status=400) # 检查邮箱格式 import re if not re.match(r'^[^\s@]+@[^\s@]+\.[^\s@]+$', email): messages.error(request, '请输入有效的邮箱地址!') return render(request, 'register.html', { 'username': username, 'email': email, 'error': 'email_invalid' }, status=400) # 检查密码复杂度 special_char_regex = re.compile(r'[!@#$%^&*(),.?":{}|<>]') if len(password1) < 8 or not special_char_regex.search(password1): messages.error(request, '密码长度至少为8个字符,且必须包含特殊字符!') return render(request, 'register.html', { 'username': username, 'email': email, 'error': 'password_weak' }, status=400) # 创建新用户 try: user = User.objects.create_user(username=username, email=email, password=password1) UserInfo.objects.create(user=user, email=email) messages.success(request, '注册成功!') return redirect('login') except Exception as e: messages.error(request, f'注册失败:{str(e)}') return render(request, 'register.html', status=500) return render(request, 'register.html') import logging logger = logging.getLogger(__name__) def user_login(request): if request.method == 'POST': form = AuthenticationForm(request, data=request.POST) if form.is_valid(): username = form.cleaned_data.get('username') password = form.cleaned_data.get('password') user = authenticate(username=username, password=password) if user is not None: login(request, user) next_url = request.GET.get('next') if next_url: return redirect(next_url) return redirect('home') else: logger.error(f"认证失败,用户名: {username}") messages.error(request, '用户名或密码错误,请重试。') else: form = AuthenticationForm() return render(request, 'login.html', {'form': form}) @login_required def user_logout(request): logout(request) messages.success(request, '您已成功退出登录。') return redirect('login') def check_username(request): username = request.GET.get('username') exists = UserInfo.objects.filter(user__username=username).exists() return JsonResponse({ 'exists': exists, 'valid': len(username) >= 3 if username else False }) def check_email(request): email = request.GET.get('email') exists = UserInfo.objects.filter(email=email).exists() import re return JsonResponse({ 'exists': exists, 'valid': bool(re.match(r'^[^\s@]+@[^\s@]+\.[^\s@]+$', email)) if email else False }) def password_reset(request): if request.method == 'POST': form = PasswordResetForm(request.POST) if form.is_valid(): email = form.cleaned_data.get('email') try: user = User.objects.get(email=email) # 生成重置密码的 token 和 uid token = default_token_generator.make_token(user) uid = urlsafe_base64_encode(force_bytes(user.pk)) # 构建重置密码的链接 from django.urls import reverse reset_url = request.build_absolute_uri(reverse('password_reset_confirm', args=[uid, token])) # 发送重置密码的邮件 send_mail( '重置密码', f'请点击以下链接重置您的密码:{reset_url}', settings.EMAIL_HOST_USER, [email], fail_silently=False, ) messages.success(request, '重置密码的邮件已发送,请检查您的邮箱。') return redirect('login') except User.DoesNotExist: messages.error(request, '该邮箱未注册。') else: form = PasswordResetForm() return render(request, 'password_reset.html', {'form': form}) def password_reset_confirm(request, uidb64, token): try: uid = force_str(urlsafe_base64_decode(uidb64)) user = User.objects.get(pk=uid) except (TypeError, ValueError, OverflowError, User.DoesNotExist): user = None if user is not None and default_token_generator.check_token(user, token): if request.method == 'POST': new_password1 = request.POST.get('new_password1') new_password2 = request.POST.get('new_password2') if new_password1 == new_password2: user.set_password(new_password1) user.save() messages.success(request, '密码重置成功,请使用新密码登录。') return redirect('login') else: messages.error(request, '两次输入的密码不一致,请重新输入。') return render(request, 'password_reset_confirm.html', {'uidb64': uidb64, 'token': token}) else: messages.error(request, '重置密码链接无效,请重新申请。') return redirect('password_reset') # @login_required def home(request): return render(request, 'home.html', {}) # @login_required def show_data_html(request): return render(request, 'show_data.html', {}) # @login_required def user_html(request): return render(request, 'user.html', {}) # @login_required def test_html(request): return render(request, 'test.html', {}) # @login_required def detection_html(request): return render(request, 'detection.html', {}) # @login_required def detection_CB_html(request): return render(request, 'detection_CB.html', {}) # @login_required def detection_video_html(request): return render(request, 'detection_video.html', {}) # @login_required def sjdsjksjskfs_html(request): return render(request, 'sjdsjksjskfs.html', {}) # @login_required def upload_video(request): if request.method == 'POST': try: video_file = request.FILES.get('video') video_id = request.POST.get('video_id') if not video_file or not video_id: return JsonResponse({'status': 'error', 'message': '缺少参数'}) # 确保临时目录存在 temp_dir = os.path.join(settings.BASE_DIR, 'temp', 'videos') if not os.path.exists(temp_dir): os.makedirs(temp_dir) # 保存视频文件 video_path = os.path.join(temp_dir, f"{video_id}.mp4") with open(video_path, 'wb+') as destination: for chunk in video_file.chunks(): destination.write(chunk) return JsonResponse({'status': 'success', 'video_id': video_id}) except Exception as e: return JsonResponse({'status': 'error', 'message': str(e)}) return JsonResponse({'status': 'error', 'message': '无效请求'}, status=405) @csrf_exempt def detect_video(request): if request.method == 'POST': # 获取视频文件 video_file = request.FILES.get('video') if video_file: # 保存视频文件到临时目录 video_path = os.path.join('temp', video_file.name) with open(video_path, 'wb+') as destination: for chunk in video_file.chunks(): destination.write(chunk) # 模型路径 model_path = os.path.join(settings.BASE_DIR, "Behaviewer/models/best.pt") # 输出视频路径 output_video_dir = os.path.join(settings.MEDIA_ROOT, 'videos') if not os.path.exists(output_video_dir): os.makedirs(output_video_dir) output_video_path = os.path.join(output_video_dir, video_file.name) # 调用函数进行视频目标检测 detect_objects_in_video(model_path, video_path, output_video_path, show=False) # 删除临时视频文件 os.remove(video_path) # 返回检测好的视频文件的路径 output_video_url = f'{settings.MEDIA_URL}videos/{os.path.basename(output_video_path)}' return JsonResponse({'success': True, 'output_video_url': output_video_url}) else: return JsonResponse({'success': False, 'message': '未找到视频文件'}) else: return JsonResponse({'success': False, 'message': '只支持POST请求'}) # 错步页面模型调用检测 def detect_objects_in_video(model_path, video_path, output_path=None, show=True): # 加载预训练模型 model = YOLO(model_path) # 打开视频文件 cap = cv2.VideoCapture(video_path) # 获取视频的帧率、宽度和高度 fps = cap.get(cv2.CAP_PROP_FPS) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 如果指定了输出路径,创建视频写入对象 if output_path: fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter(output_path, fourcc, fps, (width, height)) frame_count = 0 total_time = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break # 记录开始时间 start_time = time.time() # 进行目标检测 results = model(frame) # 记录结束时间并累加耗时 end_time = time.time() total_time += end_time - start_time frame_count += 1 # 获取检测结果并绘制到帧上 annotated_frame = results[0].plot() # 计算并显示FPS fps_current = 1 / (end_time - start_time) cv2.putText(annotated_frame, f"FPS: {fps_current:.2f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) # 保存帧到输出视频 if output_path: out.write(annotated_frame) # 显示结果 if show: cv2.imshow("Object Detection", annotated_frame) if cv2.waitKey(1) & 0xFF == ord('q'): break # 释放资源 cap.release() if output_path: out.release() cv2.destroyAllWindows() # 计算平均FPS if frame_count > 0: avg_fps = frame_count / total_time print(f"处理完成! 平均FPS: {avg_fps:.2f}") print(f"总帧数: {frame_count}") print(f"总耗时: {total_time:.2f}秒") #模态框 #框选目标 #deepseek版 # @csrf_exempt # def detect_target(request): # if request.method == 'POST': # try: # # 获取POST数据 # data = json.loads(request.body) # image_data = data['image'] # roi_data = data['roi'] # # # 从Base64提取图像数据 # header, encoded = image_data.split(",", 1) # image_bytes = base64.b64decode(encoded) # nparr = np.frombuffer(image_bytes, np.uint8) # frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) # # # 准备配置区域(根据ROI) # x, y, w, h = roi_data['x'], roi_data['y'], roi_data['width'], roi_data['height'] # points = [[x, y], [x + w, y], [x + w, y + h], [x, y + h]] # # # 配置检测区域(格式要求与page_4_1_auto_set.py一致) # config_area_current = {'观察区1': [[points]]} # # # 调用检测算法(使用默认参数) # processed_frame, mean_value = image_detect( # frame, # config_area_current, # currentback=0, # 0=检测比背景暗的目标 # kernal_erode=1, # kernal_dilate=1, # kernal_erode_2=1, # min_area=1, # max_area=10000, # adjust_threshold=15 # ) # # # 将处理后的图像转换为Base64 # _, buffer = cv2.imencode('.jpg', processed_frame) # processed_image_base64 = base64.b64encode(buffer).decode('utf-8') # # return JsonResponse({ # 'success': True, # 'processed_image': processed_image_base64, # 'mean_value': mean_value # }) # except Exception as e: # return JsonResponse({ # 'success': False, # 'message': str(e) # }) # return JsonResponse({'success': False, 'message': 'Invalid request'}) @csrf_exempt def detect_target(request): if request.method == 'POST': try: # 获取POST数据 data = json.loads(request.body) image_data = data['image'] roi_data = data['roi'] params = data.get('params', {}) # 从Base64提取图像数据 header, encoded = image_data.split(",", 1) image_bytes = base64.b64decode(encoded) nparr = np.frombuffer(image_bytes, np.uint8) frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) # 准备配置区域(根据ROI) x, y, w, h = roi_data['x'], roi_data['y'], roi_data['width'], roi_data['height'] points = [[x, y], [x + w, y], [x + w, y + h], [x, y + h]] # 配置检测区域 config_area_current = {'观察区1': [[points]]} # 调用检测算法 processed_frame, mean_value = image_detect( frame, config_area_current, currentback=params.get('currentback', 0), kernal_erode=params.get('kernal_erode', 1), kernal_dilate=params.get('kernal_dilate', 1), kernal_erode_2=params.get('kernal_erode_2', 1), min_area=params.get('min_area', 1), max_area=params.get('max_area', 10000), adjust_threshold=params.get('adjust_threshold', 15) ) # 将处理后的图像转换为Base64 _, buffer = cv2.imencode('.jpg', processed_frame) processed_image_base64 = base64.b64encode(buffer).decode('utf-8') return JsonResponse({ 'success': True, 'processed_image': processed_image_base64, 'mean_value': mean_value }) except Exception as e: return JsonResponse({ 'success': False, 'message': str(e) }) return JsonResponse({'success': False, 'message': 'Invalid request'}) # 添加新的视频处理视图 @csrf_exempt def process_video_stream(request): if request.method == 'POST': try: data = json.loads(request.body) video_id = data['video_id'] roi_data = data['roi'] params = data.get('params', {}) # 获取视频路径 video_path = os.path.join(settings.BASE_DIR, 'temp', 'videos', f"{video_id}.mp4") if not os.path.exists(video_path): return JsonResponse({'success': False, 'message': 'Video file not found'}) # 准备ROI配置 x, y, w, h = roi_data['x'], roi_data['y'], roi_data['width'], roi_data['height'] points = [[x, y], [x + w, y], [x + w, y + h], [x, y + h]] config_area = {'观察区': [[points]]} # 创建流式响应 response = StreamingHttpResponse( video_processing_generator(video_path, config_area, params), content_type='multipart/x-mixed-replace; boundary=frame' ) return response except Exception as e: return JsonResponse({'success': False, 'message': str(e)}) return JsonResponse({'success': False, 'message': 'Invalid request method'}) # 视频处理生成器 def video_processing_generator(video_path, config_area, params): cap = cv2.VideoCapture(video_path) # 获取视频信息 fps = cap.get(cv2.CAP_PROP_FPS) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) while cap.isOpened(): ret, frame = cap.read() if not ret: break # 处理当前帧 processed_frame, _ = image_detect( frame, config_area, currentback=params.get('currentback', 0), kernal_erode=params.get('kernal_erode', 1), kernal_dilate=params.get('kernal_dilate', 1), kernal_erode_2=params.get('kernal_erode_2', 1), min_area=params.get('min_area', 1), max_area=params.get('max_area', 10000), adjust_threshold=params.get('adjust_threshold', 15) ) # 转换为JPEG格式 _, buffer = cv2.imencode('.jpg', processed_frame) frame_data = buffer.tobytes() # 以MJPEG格式发送帧 yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame_data + b'\r\n') cap.release() 报错2025-07-30 11:30:09,399 WARNING D:\anaconda\envs\All\lib\site-packages\django\http\response.py:517: builtins.Warning: StreamingHttpResponse must consume synchronous iterators in order to serve them asynchronously. Use an asynchronous iterator instead
07-31
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值