设计模式实现三方登录

1 开发模式和设计模式

1.1 开发模式

  • mvc mtv,能够帮我们解耦

1.2 设计模式

  • 基于开发模式,设计理念(一种境界)
  • 工厂模式(一种设计模式)将创建模式更加抽象化,只关心参数,至于怎么变化实现不关心

1.3 静态方法和类方法的不同

  • 静态方法不需要实例化对象,直接通过类方法调用
  • 类方法需要实例化,再调用

1.4 面向接口开发

  • 逻辑类似,但是本质又不一样的适合工厂模式

2 设计模式实现三方登录

  • 钉钉三方登录
  • gitee三方登录

2.1Django端

  • views.py
import urllib
import requests
import hmac
import base64
import time
import json
from hashlib import sha256
from rest_framework.response import Response
from rest_framework.views import APIView
from oathapp.models import OathModel
from oathapp.utils import ChooseMethod, GITEE_CLIENT_SECRET, GITEE_CLIENT_ID, GITEE_REDIRECT_URI, DINGDING_APP_SECRET, DINGDING_APP_ID
from userapp.utils import create_token


class GetUrlView(APIView):
    def get(self, request):
        t = request.GET.get('type')
        url = ChooseMethod.choose(str(t))
        return Response({'url': str(url), 'code': 200, 'type':t})


class GiteeRedirectView(APIView):

    def post(self, request):
        code = request.data.get('code')
        data = {
            'client_id': GITEE_CLIENT_ID,
            'client_secret': GITEE_CLIENT_SECRET,
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': GITEE_REDIRECT_URI,
        }
        url = 'https://gitee.com/oauth/token'
        redirect_data = requests.post(url=url, data=data).json()
        token = redirect_data['access_token']
        message = requests.get('https://gitee.com/api/v5/user?access_token=' + token).json()
        uid = message.get('id')
        try:
            oath_user = OathModel.objects.get(uid=uid, oath_type="2")
        except Exception as e:
            oath_user = None
        if oath_user:
            user = oath_user
            username = user.username
            token = create_token(user)
            return Response({'username': username, 'token':token, 'code':200})
        else:
            username = message.get('name')
            user = OathModel.objects.create(username=username, uid=uid, oath_type="2")
            oath_token = create_token(user)
            return Response({'username': username, ' oath_token': oath_token, 'code': 200})


class DingDingRedirectView(APIView):

    def post(self, request):
        code = request.data.get('code')
        t = time.time()
        timestamp = str((int(round(t * 1000))))
        signature = base64.b64encode(hmac.new(DINGDING_APP_SECRET.encode('utf-8'), timestamp.encode('utf-8'), digestmod=sha256).digest())
        payload = {'tmp_auth_code': code}
        headers = {'Content-Type': 'application/json'}
        message = requests.post('https://oapi.dingtalk.com/sns/getuserinfo_bycode?signature=' + urllib.parse.quote(
        signature.decode("utf-8")) + "&timestamp=" + timestamp + "&accessKey="+ DINGDING_APP_ID,data=json.dumps(payload), headers=headers)
        message_dict = json.loads(message.text)
        print(message_dict)
        ms = message_dict.get('user_info')['unionid']
        try:
            oath_user = OathModel.objects.get(uid=ms, oath_type="1")
        except Exception as e:
            oath_user = None
        if oath_user:
            nick = message_dict.get('user_info')['nick']
            token = create_token(oath_user)
            return Response({'username': nick, 'oath_token':token, 'code':200})
        else:
            username = message_dict.get('user_info')['nick']
            user = OathModel.objects.create(username=username, uid=ms, oath_type="1")
            oath_token = create_token(user)
            return Response({'username': username, ' oath_token': oath_token, 'code': 200})
  • utils.py
# -*- coding: utf-8 -*-
# Gitee
GITEE_CLIENT_ID = '3c87c87eada7f893aa6eff464388aed8a8549a261d4289c4b112af935fd12e2e'
GITEE_REDIRECT_URI = 'http://127.0.0.1:8080/index?type=2'
GITEE_CLIENT_SECRET = '5d7e49dfb57aa15015015bef0fcf9e139fa000fe43af1ac18d9776199b15983d'

# DingDing
DINGDING_APP_ID = 'dingoayjscr9lrrmppm9en'
DINGDING_REDIRECT_URI = 'http://127.0.0.1:8080/index?type=aaa'
DINGDING_APP_SECRET = 'oczDpVdg4IGfX_JfQNMJnFA338sHrMOHvH1BrGqyM0vG8vYQGemcaAnutCTSORrT'

class GiteeFirst:
    def __repr__(self):
        return 'https://gitee.com/oauth/authorize?client_id={}&redirect_uri={}&response_type=code'.format(GITEE_CLIENT_ID, GITEE_REDIRECT_URI)


class DingDingFirst:
    def __repr__(self):
        return 'https://oapi.dingtalk.com/connect/qrconnect?appid={}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri={}'.format(DINGDING_APP_ID, DINGDING_REDIRECT_URI)


class FaceBookFirst:
    def __repr__(self):
        return 222


class ChooseMethod:
    @staticmethod
    def choose(type):
        if type == '2':
            num = GiteeFirst()
            return num
        if type == '3':
            return FaceBookFirst()
        if type == '1':
            return DingDingFirst()

2.2 Vue端

  • Login.vue
<template>
    <div style="margin-top:200px">
        <center><h1 style="margin-top:50px">登&emsp;录</h1></center>
        <div style="margin:0 auto;500px;height:200px">
            用户名:
                <a-input placeholder="input your username" style="300px" v-model="username"/>
            <br>
            <br>
            密&ensp;&ensp;码:
                <a-input-password placeholder="input your password" style="300px" v-model="password"/>
                <br>
                <br>
          <p>
              <a-input type="text" v-model="message_code"  style="200px"/>&emsp;<a-button type="primary"  @click="getMessageCode">钉钉获取验证码</a-button>
          </p>
              <a-button type="primary" @click="login">登录</a-button>
        </div>
        <a-button @click="giteeGo">Gitee</a-button>&emsp;&emsp;<a-button @click="DingDingGo">DingDing</a-button>&emsp;&emsp;<a-button>Facebook</a-button>
    </div>
</template>

<script>
import { get_message_code, user_login, get_oath_url } from '@/http/apis'
export default {
    data() {
        return {
            username:'',
            password:'',
            message_code:'',
        }
    },
    methods: {
        // 获取验证码
        getMessageCode(){
            get_message_code().then(res=>{
                console.log(res)
                res.code == 200?alert('获取验证码成功')&sessionStorage.setItem('session_key', res.session_key):alert('失败失败')
                // res.code == 200?alert('获取验证码成功'):alert('失败失败')
            })
        },
        // 登录
        login(){
            let user_info = {
                'username':this.username,
                'password':this.password,
                'num':this.message_code,
                'session_key': sessionStorage.getItem('session_key')
            }
            user_login(user_info).then(
                res=>{
                   console.log(res) 
                   res.code == 200?alert('登录成功'):alert('登录失败')
                }
            )
        },
        // gitee获取三方地址
        giteeGo(){
            get_oath_url({'type': '2'}).then(res=>{
                console.log(res)
                window.location.href = res.url
                
            })
            // window.location.href='http://127.0.0.1:8080/index'
        },
        // dingding获取三方地址
        DingDingGo(){
            get_oath_url({'type':'1'}).then(res=>{
                console.log(res.url)
                window.location.href=res.url
            })
        }
        },

    created() {

    }
}
</script>

<style scoped>

</style>
  • Index.vue
<template>
    <div>
        <h3>欢迎您访问,亲爱的{{username}}</h3>
    </div>
</template>

<script>
// import { get_gitee_redirect_url, get_dingding_redirect_url } from '@/http/apis'

export default {
    data() {
        return {
            // 后端自动跳转,所以不用这种方法啦 
            username:'',
            token:this.$route.query.code,
            type:this.$route.query.type,
        }
    },
    methods: {
        getGiteeRedirectUrl(){
            get_gitee_redirect_url({'code': this.code}).then(res=>{
                console.log(res)
                this.username = res.username
                sessionStorage.setItem('oath_token', res.oath_token)
            })
        },
        getDingDingRedirectUrl(){
            get_dingding_redirect_url({'code': this.code}).then(res=>{
                console.log(res)
                this.username = res.username
                sessionStorage.setItem('oath_token', res.oath_token)
            })
        },
    },
    created() {
        if(this.type==2){
            this.getGiteeRedirectUrl()
        }else if(this.type==null){
            this.getDingDingRedirectUrl()
        }else{
            console.log(2222222222222)
        }
        sessionStorage.setItem('oath_token', this.oath_token)
    }
}
</script>

<style scoped>

</style>

3 设计模式实现三方登录(改进版)

  • 回调地址写第二个视图函数地址,第一次拼接地址直接跳转第二个方法,第二次视图函数返回值是一个拼接好的路由,直接跳转前端页面,这种改良少了一个接口!!!

3.1 Django端

  • views.py
import urllib
import requests
import hmac
import base64
import time
import json
from hashlib import sha256
from django.shortcuts import redirect
from rest_framework.response import Response
from rest_framework.views import APIView
from oathapp.models import OathModel
from oathapp.utils import ChooseMethod, GITEE_CLIENT_SECRET, GITEE_CLIENT_ID, GITEE_REDIRECT_URI, DINGDING_APP_SECRET, DINGDING_APP_ID
from userapp.utils import create_token



class GetUrlView(APIView):
    def get(self, request):
        t = request.GET.get('type')
        url = ChooseMethod.choose(str(t))
        return Response({'url': str(url), 'code': 200, 'type':t})


class GiteeRedirectView(APIView):

    def get(self, request):
        code = request.GET.get('code')
        data = {
            'client_id': GITEE_CLIENT_ID,
            'client_secret': GITEE_CLIENT_SECRET,
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': GITEE_REDIRECT_URI,
        }
        url = 'https://gitee.com/oauth/token'
        redirect_data = requests.post(url=url, data=data).json()
        token = redirect_data['access_token']
        message = requests.get('https://gitee.com/api/v5/user?access_token=' + token).json()
        uid = message.get('id')
        try:
            oath_user = OathModel.objects.get(uid=uid, oath_type="2")
        except Exception as e:
            oath_user = None
        if oath_user:
            user = oath_user
            username = user.username
            oath_token = create_token(user)
            return redirect('http://127.0.0.1:8080/index/?token=' + oath_token + "&username=" + username)
        else:
            username = message.get('name')
            user = OathModel.objects.create(username=username, uid=uid, oath_type="2")
            oath_token = create_token(user)
            return redirect('http://127.0.0.1:8080/index/?token=' + oath_token + "&username=" + username)


class DingDingRedirectView(APIView):

    def get(self, request):
        code = request.GET.get('code')
        t = time.time()
        timestamp = str((int(round(t * 1000))))
        signature = base64.b64encode(hmac.new(DINGDING_APP_SECRET.encode('utf-8'), timestamp.encode('utf-8'), digestmod=sha256).digest())
        payload = {'tmp_auth_code': code}
        headers = {'Content-Type': 'application/json'}
        message = requests.post('https://oapi.dingtalk.com/sns/getuserinfo_bycode?signature=' + urllib.parse.quote(
        signature.decode("utf-8")) + "&timestamp=" + timestamp + "&accessKey="+ DINGDING_APP_ID,data=json.dumps(payload), headers=headers)
        message_dict = json.loads(message.text)
        print(message_dict)
        ms = message_dict.get('user_info')['unionid']
        try:
            oath_user = OathModel.objects.get(uid=ms, oath_type="1")
        except Exception as e:
            oath_user = None
        if oath_user:
            nick = message_dict.get('user_info')['nick']
            oath_token = create_token(oath_user)
            return redirect('http://127.0.0.1:8080/index/?token=' + str(oath_token) + "&username=" + nick)
        else:
            nick = message_dict.get('user_info')['nick']
            user = OathModel.objects.create(username=nick, uid=ms, oath_type="1")
            oath_token = create_token(user)
            return redirect('http://127.0.0.1:8080/index/?token=' + str(oath_token) + "&username=" + nick)
  • utils.py
# -*- coding: utf-8 -*-
# Gitee
GITEE_CLIENT_ID = '3c87c87eada7f893aa6eff464388aed8a8549a261d4289c4b112af935fd12e2e'
GITEE_REDIRECT_URI = 'http://192.168.56.100:4184/oath/redirect_gitee_url/'
GITEE_CLIENT_SECRET = '5d7e49dfb57aa15015015bef0fcf9e139fa000fe43af1ac18d9776199b15983d'

# DingDing
DINGDING_APP_ID = 'dingoayjscr9lrrmppm9en'
DINGDING_REDIRECT_URI = 'http://192.168.56.100:4184/oath/redirect_dingding_url/'
DINGDING_APP_SECRET = 'oczDpVdg4IGfX_JfQNMJnFA338sHrMOHvH1BrGqyM0vG8vYQGemcaAnutCTSORrT'

class GiteeFirst:
    def __repr__(self):
        return 'https://gitee.com/oauth/authorize?client_id={}&redirect_uri={}&response_type=code'.format(GITEE_CLIENT_ID, GITEE_REDIRECT_URI)


class DingDingFirst:
    def __repr__(self):
        return 'https://oapi.dingtalk.com/connect/qrconnect?appid={}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri={}'.format(DINGDING_APP_ID, DINGDING_REDIRECT_URI)


class FaceBookFirst:
    def __repr__(self):
        return 222


class ChooseMethod:
    @staticmethod
    def choose(type):
        if type == '2':
            num = GiteeFirst()
            return num
        if type == '3':
            return FaceBookFirst()
        if type == '1':
            return DingDingFirst()

3.2 Vue端

  • Login.vue
<template>
    <div style="margin-top:200px">
        <center><h1 style="margin-top:50px">登&emsp;录</h1></center>
        <div style="margin:0 auto;500px;height:200px">
            用户名:
                <a-input placeholder="input your username" style="300px" v-model="username"/>
            <br>
            <br>
            密&ensp;&ensp;码:
                <a-input-password placeholder="input your password" style="300px" v-model="password"/>
                <br>
                <br>
          <p>
              <a-input type="text" v-model="message_code"  style="200px"/>&emsp;<a-button type="primary"  @click="getMessageCode">钉钉获取验证码</a-button>
          </p>
              <a-button type="primary" @click="login">登录</a-button>
        </div>
        <a-button @click="giteeGo">Gitee</a-button>&emsp;&emsp;<a-button @click="DingDingGo">DingDing</a-button>&emsp;&emsp;<a-button>Facebook</a-button>
    </div>
</template>

<script>
import { get_message_code, user_login, get_oath_url } from '@/http/apis'
export default {
    data() {
        return {
            username:'',
            password:'',
            message_code:'',
        }
    },
    methods: {
        // 获取验证码
        getMessageCode(){
            get_message_code().then(res=>{
                console.log(res)
                res.code == 200?alert('获取验证码成功')&sessionStorage.setItem('session_key', res.session_key):alert('失败失败')
                // res.code == 200?alert('获取验证码成功'):alert('失败失败')
            })
        },
        // 登录
        login(){
            let user_info = {
                'username':this.username,
                'password':this.password,
                'num':this.message_code,
                'session_key': sessionStorage.getItem('session_key')
            }
            user_login(user_info).then(
                res=>{
                   console.log(res) 
                   res.code == 200?alert('登录成功'):alert('登录失败')
                }
            )
        },
        // gitee获取三方地址
        giteeGo(){
            get_oath_url({'type': '2'}).then(res=>{
                console.log(res)
                window.location.href = res.url
                
            })
        },
        // dingding获取三方地址
        DingDingGo(){
            get_oath_url({'type':'1'}).then(res=>{
                console.log(res.url)
                window.location.href=res.url
            })
        }
        },

    created() {

    }
}
</script>

<style scoped>

</style>
  • Index.vue
<template>
    <div>
        <h3>欢迎您访问,亲爱的{{username}}</h3>
    </div>
</template>

<script>

export default {
    data() {
        return {
            username:this.$route.query.username,
            oath_token:this.$route.query.oath_token
        }
    },
    methods: {

    },
    created() {
        sessionStorage.setItem('oath_token', this.oath_token)
    }
}
</script>

<style scoped>

</style>

4 跨域问题

image-20201217092343364

4.1 解决方案

改为
CORS_ORIGIN_WHITELIST = (
    'http://127.0.0.1:8080',
    'http://localhost:8080',
)
CORS_ALLOW_CREDENTIALS = True

不能写
CORS_ORIGIN_ALLOW_ALL = True

4.2 用终端运行

^C(syl) root@dev:gogogo# python manage.py runserver 192.168.56.100:4184
原文地址:https://www.cnblogs.com/mapel1594184/p/14169401.html