Django pc app端微信登录

最近项目中需要微信登录,不仅要pc端登录,还要有app端的登录,今天,先记录一下pc端微信登录,首先我查看了微信登录的官网,里面写了好多,但是到最后总结,其实说了好多废话,好多步骤都是用不要的,其实只是需要两步就可以了:

  1. 向微信请求url,什么意思哪,就是后台需要请求一个链接,拿到链接之后,发送给前端,前端拿到这个url之后,发送请求,这样就会得到一个一个二维码,用户会可以扫这个二维码进行登录
class WeChatAuthURLView(APIView):
    """获取微信登录url(获取code)"""

    def get(self, request):
        # 先获取查询字符串中redirect url的值
        # next = request.query_params.get('redirect')
        oauth = OauthWeChat()
        # 获取微信code的url
        login_url = oauth.get_code_url()
        return APIResponse.success(data={'login_url': login_url}, message='OK')
import requests
import json
import random
from urllib.parse import urlencode
from itsdangerous import TimedJSONWebSignatureSerializer as TJWSSerializer, BadData
from django.conf import settings

from oauth import constents

class OauthWeChat(object):
    """微信登录工具"""


    def get_code_url(self):
        """
        获取微信登录的url
        返回说明:redirect_uri?code=CODE&state=STATE;
        请求成功返回code和state,否则只有state
        """

        redirect_uri = 'http://www.xxxxx.com/mall/perfect'
        url = 'https://open.weixin.qq.com/connect/qrconnect?'
        params = {
            'appid' : settings.WE_CHAT_APP_ID,
            'redirect_uri' : redirect_uri,
            'response_type' : 'code',
            'scope' : 'snsapi_login',
            'state' : self.generate_state(),
        }

        # resp = requests.get(url, params =params)
        params = urlencode(params)
        url = url + params
        return url
   
    def generate_state(self):
        """生成state"""

        serializer = TJWSSerializer(settings.SECRET_KEY, expires_in=constents.SAVE_WECHAT_USER_TOKEN_EXPIRES)
        num = random.randint(1, 999)
        state = serializer.dumps({'num': num}).decode()
        return state

至于请求url时,携带的参数微信文档上就有,我就不再说明了,只说一下这个参数:redirect_uri = ‘http://www.xxxx.com/mall/perfect’, 这个是什么哪,这个参数其实是前端的一个页面的地址,当用户扫码之后,用户授权登录,这时候微信就会重定向到这个页面,并且带上code和state,如’http://www.xxxx.com/mall/perfect?code=CODE&state=STATE,这时候,前端拿到code和state请求后端接口,后端拿到之后,验证state,防止csrf攻击,验证成功之后,拿到code,请求appid(其实access_token我们不需要)
2. 前端凭借code和state来访问后端,后端凭借code去向微信拿appid,拿到appid之后,去表里面查有这个appid没有,如果没有,说明用户有两种情况1.该用户注册了账号,但是这是第一次用微信登录2.该用户从来没有登录,还没有账号,不管是哪一种情况,我们直接将oppid加密返回给前端,这时候,页面redirect_uri = 'http://www.onefashionline.com/mall/perfect’就又有作用了,页面里面就让用户填写用户账号,填写之后,将oppid和用户信息一起请求后端接口,后端通过用户信息的手机号(区分用户)来查询用户注册过账号没有,注册过则将用户信息和oppid关联起来,如果没有,将用户注册并与oppid关联,第二次微信登录时,查询oppid,就能查到,这样直接办法token,状态保持即可

    def check_state(state):
        """
        检验保存用户数据的state
        :param num: num
        :return: num or None
        """
        serializer = TJWSSerializer(settings.SECRET_KEY, constents.SAVE_WECHAT_USER_TOKEN_EXPIRES)
        try:
            data = serializer.loads(state)
        except BadData:
            return None
        else:
            return data.get('num')


    def generate_bind_user_access_token(self, openid):
        """通过openid生成保存用户数据的token"""

        # 通过itsdangerous生成凭据access_token
        serializer = TJWSSerializer(settings.SECRET_KEY, expires_in=constents.SAVE_WECHAT_USER_TOKEN_EXPIRES)

        # serializer.dumps(数据), 返回bytes类型
        token = serializer.dumps({'openid': openid}).decode()

        return token

    @staticmethod
    def check_save_user_token(token):
        """
        检验保存用户数据的token
        :param token: token
        :return: openid or None
        """
        serializer = TJWSSerializer(settings.SECRET_KEY, constents.SAVE_WECHAT_USER_TOKEN_EXPIRES)
        try:
            data = serializer.loads(token)
        except BadData:
            return None
        else:
            return data.get('openid')
class WeChatAuthUserView(CreateAPIView):
    """微信登录的用户"""

    # 指定序列化器,参数校验和用户创建在序列化器中完成
    serializer_class = OAuthWeChatUserSerializer

    def get(self, request):
        # 获取code
        code = request.query_params.get('code')
        state = request.query_params.get('state','')

        # app微信登录需要携带参数type
        type = request.query_params.get('type', None)
        if type and type == 'app':
            if not code:
                return APIResponse.fail(message='登录失败,请重试')
            # 获取openid access_token等参数
            result = AppOauthWeChat().get_access_token(code)
            if result:
                openid = result.get('openid')
            else:
                return APIResponse.fail(message="微信服务器异常")
            # 根据openID判断用户是否是第一次登陆
            wechat_user = OAuthWeChatUser.objects.filter(openid=openid).first()

            # 如果用户不存在就是第一次登陆,通过openid生成access_token返回,用来标记用户身份
            if not wechat_user:
                # 此处使用pc端的加密,因为校验在序列化器中校验,使用的是pc端的,要保持一致
                token = OauthWeChat().generate_bind_user_access_token(openid)
                data = {"access_token": token}
                return Response({'code': 301, 'data': data, 'message': 'OK'})

            # 如果不是,签发JWTtoken,保存用户状态
            # 获取user对象
            user = wechat_user.user
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)

            # 更改登录时间
            now_time = datetime.datetime.now()
            User.objects.filter(id=user.id).update(last_login=now_time)

            response = {
                'token': token,
                'user_id': user.id,
                'role': user.role,
                'username': user.username
            }
            return APIResponse.success(data=response, message='OK')
        
        # pc端登录
        oauth = OauthWeChat()
        if not oauth.check_state(state):
            return APIResponse.fail(message='错误的请求')

        if not code:
            return APIResponse(message="登录失败,请重试")
        # 根据code获取openID,  可以在工具类中定义获取openID的方法

        # access_token, openid = oauth.get_access_token(code)
        result = oauth.get_access_token(code)
        if result:
            # access_token = result.get('access_token')
            openid = result.get('openid')
        else:
            return APIResponse.fail(message="微信服务器异常")


        # 根据openID判断用户是否是第一次登陆
        wechat_user = OAuthWeChatUser.objects.filter(openid=openid).first()

        # 如果用户不存在就是第一次登陆,通过openid生成access_token返回,用来标记用户身份
        if not wechat_user:
            token = oauth.generate_bind_user_access_token(openid)
            # 添加用户信息
            # return APIResponse.success(data={"access_token": token},message='OK')
            data = {"access_token": token}
            return Response({'code':301,'data':data,'message':'OK'})

        # 如果不是,签发JWTtoken,保存用户状态
        # 获取user对象
        user = wechat_user.user
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)

        # 更改登录时间
        now_time = datetime.datetime.now()
        User.objects.filter(id=user.id).update(last_login=now_time)

        response = {
            'token': token,
            'user_id': user.id,
            'role':user.role,
            'username': user.username
        }
        return APIResponse.success(data=response,message='OK')


    @transaction.atomic
    def create(self, request, *args, **kwargs):
        """绑定微信用户"""

        serializer = self.get_serializer(data=request.data)
        if not serializer.is_valid():
            message = ','.join(
                serializer.errors[key] for key in serializer.errors if isinstance(serializer.errors[key], str))
            if not message:
                message = ','.join(
                    serializer.errors[key][0] for key in serializer.errors if isinstance(serializer.errors[key], list))
            return APIResponse.fail(message=message)

        data = serializer.validated_data
        mobile = data['mobile']
        openid = data['openid']
        user = User.objects.filter(mobile=mobile).first()

        if not user:
            username = '用户_' + mobile[-4:]
            user = User.objects.create_user(username=username, mobile= mobile, password=data['password'],role=1)
            share_url_token = user.generate_token()
            UserInfo.objects.create(user=user, auth_state=0, share_url_token=share_url_token)

            # 添加用户注册IP归属地
            x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')

            if x_forwarded_for:
                ip = x_forwarded_for.split(',')[0]  # 这里是真实的ip
            else:
                ip = request.META.get('REMOTE_ADDR')  # 这里获得代理ip

            # 注册时根据ip来查询所在地(此时类型先写死为web)
            ip_home(user.id, ip, 1)

        else:
            user.password = make_password(data['password'])
            user.save()

        OAuthWeChatUser.objects.create(user=user, openid = openid)
        # 签发jwt token
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)

        # 更改登录时间
        now_time = datetime.datetime.now()
        User.objects.filter(id=user.id).update(last_login=now_time)

        response = {
            'token': token,
            'user_id': user.id,
            'role': user.role,
            'username': user.username
        }
        return APIResponse.success(data=response, message='OK')
  1. 可以看到,这里面还有app登录,app登录和pc端登录不同的时,第一步的操作,就是请求code不需要后端操作,这里app前端会唤醒微信,直接拿到code,然后带上code直接就从第二步开始了
    4.微信开放平台网址:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值