目次
Django は DRF + Simple JWT を使用して、カスタム ユーザーを使用する小さなプログラムの登録、ログイン、および認証を完了します。
djangorestframework-simplejwt が設定されている前提で
モデル クラスとシリアライザー
アプレット ユーザー モデル クラス
ここでのモデル クラスは django に付属するユーザー モデル クラスを継承していません. 利点は、後でアプレットのユーザーが管理者側にアクセスできないことです. 欠点は、django に付属するパーミッション管理に影響を与える可能性があることです.ユーザーがアプレット側にいる場合は問題ありませんが、他のユーザーがいる場合は問題が発生する可能性があります。このモデルにはパスワードがないため、アプレットによって生成されたユーザーのコードを取得する限り、ユーザーのコードを取得できます。ログインインターフェイスを介したトークン (小さなプログラム 端末上のコードは生成されるたびに異なり、有効期間は 5 分である必要があります)
class User(models.Model):
USER_TYPE = ((1, '顾客'),
(2, '商家'))
GENDER = ((0, '男'),
(1, '女'),
(2, '未知'))
username = models.CharField('用户名', max_length=20, blank=True)
tel = models.BigIntegerField('手机号', unique=True, blank=True, null=True)
openid = models.CharField('小程序openid', unique=True, max_length=100, blank=True, null=True)
avatar_url = models.URLField('头像', help_text='头像', null=True, blank=True)
unionid = models.CharField('小程序unionid', unique=True, max_length=100, blank=True, null=True)
nickname = models.CharField('微信昵称', max_length=100, blank=True, null=True)
gender = models.IntegerField('性别', choices=GENDER, default=2)
type = models.IntegerField('用户类型', choices=USER_TYPE, default=1)
# 用自定义的用户需要重写此方法
@property
def is_authenticated(self):
"""
Always return True. This is a way to tell if the user has been
authenticated in templates.
"""
return True
def __str__(self):
return self.openid
class Meta:
db_table = 'user'
verbose_name = '用户管理'
verbose_name_plural = verbose_name
アプレット ユーザー シリアライザー
もちろん、アプレットのユーザー テーブル内のすべての情報は、登録時間や更新時間、最終ログイン時間など、自分でモデル クラスに追加できます。これは単なるデモンストレーションであり、追加するものではありません。
class UserSerializer(serializers.ModelSerializer):
"""用户序列化器"""
class Meta:
model = User
fields = '__all__'
カスタム認証クラス
主にユーザーの取得に使用され、その他のメソッドは simplejwt の JWTAuthentication のメソッドを継承しています。
ユーザー app ディレクトリに新しいファイル Authentication.py を直接作成し、次の内容を記述します。
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken, AuthenticationFailed
from apps.user.models import User
# 自定义的解析token的方法 下面会用到
from utils.token_get_user import get_user
class MyJWTAuthentication(JWTAuthentication):
"""
继承JWTAuthentication类, 返回自定义User对象
"""
# get_user用于返回用户,重写后在其它地方使用request.user时可以直接得到自定义的小程序用户
def get_user(self, validated_token):
try:
user_id = get_user_id(str(validated_token)) # 此处为自定义的解析token方法, 可以解码token得到其中的信息,重点是拿到user_id 用于后续的获取用户
except KeyError:
raise InvalidToken(_('Token不包含可识别的用户标识'))
try:
user = User.objects.get(**{'id': user_id})
except User.DoesNotExist:
raise AuthenticationFailed(_('未找到用户'), code='user_not_found')
return user
get_user_id メソッド: トークンを解析して user_id を取得するために使用されます
import jwt
import time
# ConvLife为我的项目名称,此处导入的是项目的settings,主要是拿到自定义的secret_key
from ConvLife import settings
def get_user_id(t):
"""根据token得到当前用户user_id"""
try:
decode_data = jwt.decode(t, secret_key=settings.SECRET_KEY, verify=False, algorithms=['HS256'])
print(decode_data)
if int(decode_data['exp']) < int(time.time()):
return "token过期"
return decode_data['user_id']
except Exception as e:
return "token错误:\n"+str(e)
ミニプログラムのログインとトークンの手動発行
ユーザービュー
ログイン時、これは実際には create を書き換えています. アイデアは大まかに: ユーザーによって渡されたコードを通じて openid を取得します (openid が取得されない場合は、コードが間違っているか、ユーザーがコードを入力していないことを意味します) 、次にopenidを使用してユーザーテーブルでユーザーを見つけます.存在しない場合は新しいユーザーを作成し、存在する場合はそれを読み取り、読み取ったユーザーまたは新しく作成したユーザーのトークンを生成し、それをフロントエンド
# get_wx_openid 是调用微信开放接口,使用小程序传到后端的code去请求openid,openid作为唯一标识
from utils.wx import get_wx_openid
from utils.Authentication import MyJWTAuthentication
class WxLogin(mixins.CreateModelMixin, viewsets.GenericViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
# code是小程序端可以直接获取到的,获取到后和userinfo一起Post到后端
code = request.data.get('code', '')
# 通过传入的code来获取openid
openid_res = get_wx_openid(code)
try:
openid = openid_res['openid'] or ''
except KeyError as e:
return Response({'message': '微信调用失败'}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
# 尝试查找用户,如果用户存在则读取用户,用户不存在则新建用户
try:
user = User.objects.get(openid=openid)
except User.DoesNotExist:
# userinfo 是小程序端可以直接获取到的,获取到后和code 一起Post到后端
user_info = request.data.get('userInfo')
print(user_info)
user = User.objects.create(openid=openid,
avatar_url=user_info['avatarUrl'],
nickname=str(user_info['nickName']),
gender=user_info['gender'])
user.save()
# 手动签发jwt token
refresh = RefreshToken.for_user(user)
resp_data = {
'user_id': user.id,
"refresh": str(refresh),
"access": str(refresh.access_token)
}
return Response(resp_data)
ルーティング
ここでは、フロント エンドは、コードとユーザー情報を要求に送信するように直接構成されています. テストする必要がある場合は、最初に WeChat アプレットからコードを取得する必要があります. コードを要求に持ってくる必要があります, そうしないと、エラーが報告されます!
from .views import WxLogin
router = routers.SimpleRouter()
# 小程序用户
router.register(r'login/', WxLogin, basename="login")
urlpatterns = router.urls
間違ったコードでトークンを取得できませんでした
トークンを正常に取得するには、正しいコードを使用してください
使用
アプレット ユーザーのログイン確認が必要なビューに と をpermission_classes = [permissions.IsAuthenticated]
追加authentication_classes = (MyJWTAuthentication,)
します。ユーザーのお気に入りまたはお気に入りを取得する場合、ユーザーはログイン ユーザーである必要があり、カスタム クラスを使用します。ログイン ビューには追加しないでください。クラス、検証が必要な場合のみビュー クラスに追加されます。
例えば:
from rest_framework import mixins
from rest_framework.permissions import IsAuthenticated
from .models import UserFav
from .serializers import UserFavSerializer
from utils.Authentication import MyJWTAuthentication
class UserFavViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
"""
create: 用户收藏藏品
取消收藏商品
list: 获取收藏藏品列表
"""
queryset = UserFav.objects.all()
serializer_class = UserFavSerializer
permission_classes = [IsAuthenticated] # 增加此行
authentication_classes = [MyJWTAuthentication, ] # 增加此行
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
search_fields = ('goods__good_name', 'goods__goods_brief')
# 获取到当前用户的收藏列表
def get_queryset(self):
return UserFav.objects.filter(user=self.request.user.id)
初心者のブロガーです, 私は主に私の研究で遭遇した問題とそれらを解決するプロセスを記録したいと思います. 多くの欠陥があります. 理解できないことがある場合は, 許してください. プライベートメッセージを送信することもできます. . 見てからなるべく早く返信します。