Parcourir la source

企业微信登录

peng il y a 2 ans
Parent
commit
cda6094ac1
4 fichiers modifiés avec 409 ajouts et 6 suppressions
  1. 4 3
      VSeesResourceWeb/urls.py
  2. 245 1
      background/Object.py
  3. 14 0
      background/models.py
  4. 146 2
      background/views.py

+ 4 - 3
VSeesResourceWeb/urls.py

@@ -14,10 +14,10 @@ Including another URLconf
     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
 """
 """
 from django.conf.urls import url
 from django.conf.urls import url
-from django.contrib import admin
-from django.urls import path, include
+from django.urls import path, include, re_path
 from rest_framework import routers
 from rest_framework import routers
-from background.views import ProductInfoSet, VideoInfoSet, QuickStartInfoSet, UpgradeFirmwareInfoSet, GetUploadUrlView
+from background.views import ProductInfoSet, VideoInfoSet, QuickStartInfoSet, UpgradeFirmwareInfoSet, GetUploadUrlView, \
+    WechatLoginView
 
 
 router = routers.DefaultRouter()
 router = routers.DefaultRouter()
 router.register(r'productInfo', ProductInfoSet)
 router.register(r'productInfo', ProductInfoSet)
@@ -28,5 +28,6 @@ router.register(r'upgradeFirmwareInfo', UpgradeFirmwareInfoSet)
 urlpatterns = [
 urlpatterns = [
     # path('admin/', admin.site.urls),
     # path('admin/', admin.site.urls),
     path('getUploadUrl/', GetUploadUrlView.as_view()),
     path('getUploadUrl/', GetUploadUrlView.as_view()),
+    re_path(r'^vsees/(?P<operation>.*)', WechatLoginView.as_view()),
     url(r'^', include(router.urls)),
     url(r'^', include(router.urls)),
 ]
 ]

+ 245 - 1
background/Object.py

@@ -6,13 +6,24 @@
 @Email   : zhangdongming@asj6.wecom.work
 @Email   : zhangdongming@asj6.wecom.work
 @Software: PyCharm
 @Software: PyCharm
 """
 """
+import time
 import traceback
 import traceback
-
+import redis
 import boto3
 import boto3
 import botocore
 import botocore
 from boto3.session import Session
 from boto3.session import Session
 from botocore import client
 from botocore import client
 from botocore.exceptions import ClientError
 from botocore.exceptions import ClientError
+import jwt
+import datetime
+
+SERVER_HOST = 'backendserver.5tgle2.0001.usw1.cache.amazonaws.com'  # 国外节点
+OAUTH_ACCESS_TOKEN_SECRET = 'a+jbgnw%@1%zy^=@dn62%'
+OAUTH_REFRESH_TOKEN_SECRET = 'r+jbgnw%@1%zy^=@dn62%'
+# access_token超时
+OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(days=30)
+# refresh_token超时
+OAUTH_REFRESH_TOKEN_TIME = datetime.timedelta(days=30)
 
 
 
 
 class AmazonS3Util:
 class AmazonS3Util:
@@ -237,3 +248,236 @@ class AmazonS3Util:
             return obj['Contents']
             return obj['Contents']
         except Exception as e:
         except Exception as e:
             return []
             return []
+
+
+class RedisObject:
+
+    def __init__(self, db=0):
+        self.POOL = redis.ConnectionPool(host=SERVER_HOST, port=6379, db=db)
+        self.CONN = redis.Redis(connection_pool=self.POOL)
+
+    def set_data(self, key, val, expire=0):
+        try:
+            self.CONN.set(key, val)
+            if expire > 0:
+                self.CONN.expire(key, expire)
+        except Exception as e:
+            return False
+        else:
+            return True
+
+    def get_data(self, key):
+        try:
+            val = self.CONN.get(key)
+        except Exception as e:
+            print(repr(e))
+            return False
+        else:
+            if val:
+                return val.decode('utf-8')
+            else:
+                return False
+
+    def del_data(self, key):
+        try:
+            val = self.CONN.delete(key)
+        except Exception as e:
+            print(repr(e))
+            return False
+        else:
+            return True
+
+    def get_size(self):
+        return self.CONN.dbsize()
+
+    # 向列表插入数据
+    def rpush(self, name, val):
+        self.CONN.rpush(name, val)
+
+    def lpop(self, name):
+        val = self.CONN.lpop(name)
+        if val:
+            return val.decode('utf-8')
+        else:
+            return False
+
+    # 获取列表长度
+    def llen(self, name):
+        return self.CONN.llen(name=name)
+
+    # 获取列表所有数据
+    def lrange(self, name, start, end):
+        return self.CONN.lrange(name, start, end)
+
+    # 删除列表指定数据
+    def lrem(self, name, num, value):
+        """
+        num:列表方向,删除个数(0:所有)
+        value:删除的值
+        """
+        return self.CONN.lrem(name, num, value)
+
+    def get_ttl(self, key):
+        ttl = self.CONN.ttl(key)
+        if ttl:
+            return ttl
+        else:
+            return 0
+
+    def get_keys(self, key):
+        keys = self.CONN.keys(key)
+        if keys:
+            return keys
+        else:
+            return False
+
+    def set_ex_data(self, key, val, expire=0):
+        try:
+            self.CONN.setex(name=key, time=expire, value=val)
+        except Exception as e:
+            return False
+        else:
+            return True
+
+    def set_hash_data(self, key, kwargs):
+        self.CONN.hmset(key, kwargs)
+
+    def get_hash_data(self, key, file):
+        return self.CONN.hmget(key, file)
+
+    def get_all_hash_data(self, key):
+        return self.CONN.hgetall(key)
+
+
+class TokenObject:
+
+    def __init__(self, token=None, returntpye='currency'):
+        if token == 'local':
+            token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySUQiOiIxNTg0MzUxODk2MjgyMTM4MDAxMzgwMDAiLCJsYW5nIjoiZW4iLCJ1c2VyIjoiMTM2ODAzMTc1OTYiLCJtX2NvZGUiOiIxMjM0MTMyNDMyMTQiLCJleHAiOjE1ODcyNzcwNjB9.c0LV_XyxwbzUlYqMJqx7vw9f19Jv-0kGnUHuu_go-mo'
+        if token == 'test':
+            token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiMTM4MDAxMzgwMDEiLCJleHAiOjE1Njk5OTg4OTYsInVzZXJJRCI6IjE1MTU2NDI2MjMzNzkzOTUxMzgwMDEzODAwMSIsImxhbmciOiJlbiIsIm1fY29kZSI6IjEyMzQxMzI0MzIxNCJ9.VAQtT9AbCCfXcrNj9DL5cvVasMDoI7AP8ptgU1GoMu8'
+        self.token = token
+        self.lang = None
+        self.userID = None
+        self.user = ''
+        self.code = 0
+        self.valid()
+        self.returntpye = returntpye
+
+    def valid(self):
+        if self.token is None:
+            self.code = 309
+            return
+        try:
+            self.token = self.token.replace("Bearer ", "")
+
+            res = jwt.decode(self.token, OAUTH_ACCESS_TOKEN_SECRET, algorithms='HS256')
+            self.userID = res.get('userID', None)
+            self.lang = res.get('lang', None)
+            self.user = res.get('user', '')
+            # 刷新登录时间
+            # if self.userID:
+            #     print(self.user)
+            #     redisObj = RedisObject(db=3)
+            #     redisObj.set_data(key=self.userID, val=self.user, expire=300)
+
+        except jwt.ExpiredSignatureError as e:
+            self.code = 309
+            return
+        except Exception as e:
+            self.code = 309
+            return
+        else:
+            if not self.userID:
+                self.code = 309
+                return
+            else:
+                if self.userID:
+                    self.code = 0
+                    return res
+                else:
+                    self.code = 309
+                    return
+
+    def generate(self, data=None):
+        if data is None:
+            data = {}
+        try:
+            access_expire = int(OAUTH_ACCESS_TOKEN_TIME.total_seconds())
+            refresh_expire = int(OAUTH_REFRESH_TOKEN_TIME.total_seconds())
+            now_stamp = int(time.time())
+            access_data = data
+            refresh_data = data
+            access_data['exp'] = access_expire + now_stamp
+            refresh_data['exp'] = refresh_expire + now_stamp
+            access_token = jwt.encode(access_data,
+                                      OAUTH_ACCESS_TOKEN_SECRET,
+                                      algorithm='HS256')
+            refresh_token = jwt.encode(
+                refresh_data,
+                OAUTH_REFRESH_TOKEN_SECRET,
+                algorithm='HS256')
+
+            res = {
+                'access_token': access_token,
+                'access_expire': access_expire,
+                'refresh_expire': refresh_expire,
+                'refresh_token': refresh_token,
+            }
+
+            if self.returntpye == 'pc':
+                res = {
+                    'token': access_token,
+                    'access_expire': access_expire,
+                    'refresh_expire': refresh_expire,
+                    'refresh_token': refresh_token,
+                }
+
+        except Exception as e:
+            self.code = 309
+            print(repr(e))
+        else:
+            self.code = 0
+            return res
+
+    def encryption(self, data=None):
+        if data is None:
+            data = {}
+        try:
+            access_expire = int(OAUTH_ACCESS_TOKEN_TIME.total_seconds())
+            refresh_expire = int(OAUTH_REFRESH_TOKEN_TIME.total_seconds())
+            now_stamp = int(time.time())
+            access_data = data
+            refresh_data = data
+            access_data['exp'] = access_expire + now_stamp
+            refresh_data['exp'] = refresh_expire + now_stamp
+            access_token = jwt.encode(access_data,
+                                      OAUTH_ACCESS_TOKEN_SECRET,
+                                      algorithm='HS256')
+            return access_token
+        except Exception as e:
+            self.code = 309
+            print(repr(e))
+
+    def refresh(self):
+        if not self.token:
+            self.code = 309
+            return
+        try:
+            res = jwt.decode(self.token, OAUTH_REFRESH_TOKEN_SECRET, algorithms='HS256')
+        except jwt.ExpiredSignatureError as e:
+            print('过期')
+            print(repr(e))
+            self.code = 309
+        except Exception as e:
+            self.code = 309
+            print(repr(e))
+        else:
+            self.code = 0
+            userID = res.get('userID', '')
+            user = res.get('user', '')
+            lang = self.lang
+            self.userID = userID
+            self.user = user
+            refreshRes = self.generate(data={'userID': userID, 'lang': lang, 'user': user})
+            return refreshRes

+ 14 - 0
background/models.py

@@ -2,6 +2,20 @@
 from django.db import models
 from django.db import models
 
 
 
 
+class WechatUserInfo(models.Model):
+    user_id = models.IntegerField(primary_key=True, verbose_name=u'企业用户ID', unique=True)
+    name = models.CharField(blank=True, max_length=32, verbose_name=u'名字')
+    position = models.CharField(blank=True, max_length=64, verbose_name=u'岗位')
+    status = models.SmallIntegerField(default=0, verbose_name='用户状态')  # 1:已激活,2:已禁用,4:未激活,5:退出企业
+    add_time = models.IntegerField(default=0, verbose_name='创建时间')
+    upd_time = models.IntegerField(default=0, verbose_name='修改时间')
+
+    class Meta:
+        db_table = 'wechat_user_info'
+        verbose_name = '企业用户信息'
+        verbose_name_plural = verbose_name
+
+
 class ProductInfo(models.Model):
 class ProductInfo(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='主键')
     id = models.AutoField(primary_key=True, verbose_name='主键')
     product_name = models.CharField(default='', max_length=32, verbose_name='产品名称')
     product_name = models.CharField(default='', max_length=32, verbose_name='产品名称')

+ 146 - 2
background/views.py

@@ -1,11 +1,16 @@
 # Create your views here.
 # Create your views here.
+import hashlib
+import time
+import uuid
+
+import requests
 from rest_framework.views import APIView
 from rest_framework.views import APIView
 from rest_framework.viewsets import ModelViewSet
 from rest_framework.viewsets import ModelViewSet
 
 
-from background.Object import AmazonS3Util
+from background.Object import AmazonS3Util, RedisObject, TokenObject
 from background.serializers import ProductInfoSerializer, VideoInSerializer, QuickStartInfoSerializer, \
 from background.serializers import ProductInfoSerializer, VideoInSerializer, QuickStartInfoSerializer, \
     UpgradeFirmwareInfoSerializer
     UpgradeFirmwareInfoSerializer
-from background.models import ProductInfo, VideoInfo, QuickStartInfo, UpgradeFirmwareInfo
+from background.models import ProductInfo, VideoInfo, QuickStartInfo, UpgradeFirmwareInfo, WechatUserInfo
 from rest_framework.response import Response
 from rest_framework.response import Response
 
 
 
 
@@ -54,3 +59,142 @@ class GetUploadUrlView(APIView):
         except Exception as e:
         except Exception as e:
             return Response(
             return Response(
                 {'code': 500, 'result': {'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))}})
                 {'code': 500, 'result': {'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))}})
+
+
+class WechatLoginView(APIView):
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, operation, request)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, operation, request)
+
+    def validation(self, request_dict, operation, request):
+        if operation == 'get-state':  # 获取state值
+            return self.get_state()
+        elif operation == 'get-user':  # 获取用户
+            return self.get_user(request_dict)
+        else:
+            return Response({'code': 414, 'result': {'error_msg': '请求路径有误'}})
+
+    @staticmethod
+    def get_state():
+        """
+        获取state
+        @return: response
+        """
+        nwo_time = time.time()
+        redis_obj = RedisObject()
+        try:
+            state = hashlib.md5((str(uuid.uuid1()) + str(nwo_time)).encode('utf-8')).hexdigest()
+            state_statue = redis_obj.set_ex_data(state, 0, 300)  # redis记录state
+            if state_statue:
+                return Response({'code': 0, 'result': {'state': state}})
+            else:
+                return Response({'code': 120, 'result': {'error_msg': '获取status失败'}})
+        except Exception as e:
+            return Response(
+                {'code': 500, 'result': {'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))}})
+
+    @classmethod
+    def get_user(cls, request_dict):
+        """
+        获取用户信息
+        @request_dict code:唯一标识
+        @request_dict state:唯一标识
+        return:
+        """
+        code = request_dict.get('code', None)
+        state = request_dict.get('state', None)
+        if not all([code, state]):
+            return Response({'code': 444, 'result': {'error_msg': '缺少参数'}})
+        # 验证state,获取token
+        access_token = cls.get_access_token(state)
+        if not access_token:
+            return Response({'code': 120, 'result': {'error_msg': '获取token失败'}})
+        data = {
+            'access_token': access_token,
+            'code': code
+        }
+        try:
+            #  获取用户id
+            get_userid_url = 'https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo'
+            response = requests.get(get_userid_url, params=data)
+            data = response.json()
+            user_id = data.get("userid")
+            if user_id:
+                user_name = cls.add_or_update_user_info(access_token, user_id)
+                if not user_name:
+                    return Response({'code': 120, 'result': {'error_msg': '登录失败'}})
+                token_obj = TokenObject()
+                token = token_obj.generate(data={'userID': user_id, 'user': user_name})
+                return Response({'code': 0, 'result': {'token': token}})
+            else:
+                errcode = data.get('errcode')
+                errmsg = data.get('errmsg')
+                return Response({'code': errcode, 'result': {'error_msg': errmsg}})
+        except Exception as e:
+            return Response(
+                {'code': 500, 'result': {'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))}})
+
+    @classmethod
+    def get_access_token(cls, state):
+        redis_client = RedisObject()
+        state_val = redis_client.get_data(state)
+        #  验证state
+        if state_val:
+            redis_client.del_data(state)
+        else:
+            return False
+        access_token = redis_client.get_data(key='enterprise_wechat_access_token')
+        if access_token:
+            return access_token
+        data = {
+            'corpid': 'ww467ec1685e8262e6',
+            'corpsecret': 'IeUoaQ-0hEhEduCQq1zyfVXjfeZpMsThK1nklszRzUY'
+        }
+        #  获取access_token,redis中不存在该值时重新请求获取access_token(有效时长两小时)
+        token_url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken'
+        token_response = requests.get(token_url, params=data)
+        data = token_response.json()
+        if data.get('errcode') == 0:
+            access_token = data['access_token']
+            redis_client.set_data('enterprise_wechat_access_token', access_token)
+            return access_token
+        else:
+            return False
+
+    @classmethod
+    def add_or_update_user_info(cls, access_token, user_id):
+        now_time = int(time.time())
+        data = {
+            'access_token': access_token,
+            'userid': user_id
+        }
+        #  获取用户信息
+        token_url = 'https://qyapi.weixin.qq.com/cgi-bin/user/get'
+        response = requests.get(token_url, params=data)
+        data = response.json()
+        wechat_user_qs = WechatUserInfo.objects.filter(user_id=user_id)
+        if data.get('errcode') == 0:
+            url_data = {
+                'name': data.get('name'),
+                'position': data.get('position'),
+                'status': data.get('status'),
+                'add_time': now_time,
+                'upd_time': now_time
+            }
+            if not wechat_user_qs.exists():
+                url_data['user_id'] = user_id
+                WechatUserInfo.objects.create(**url_data)
+            else:
+                wechat_user_qs.update(url_data)
+            if data.get('status') == 1:
+                return data.get('name')
+            else:
+                return False
+        else:
+            return False