├── README.md ├── oauth ├── __init__.py ├── admin.py ├── forms.py ├── migrations │ ├── __init__.py │ └── __init__.pyc ├── models.py ├── oauth_client.py ├── urls.py └── views.py └── templates ├── form.html └── message.html /README.md: -------------------------------------------------------------------------------- 1 | #django-oauth 2 |
6 | 该项目是django的app应用,主要用途是使用OAuth2.0关联第三方账号。
7 |
9 | 鉴于我的django项目是采用django自带的用户认证系统,而且用户名是使用邮箱地址作为用户名。其中有些代码涉及到这两个东西,导致代码通用性一般。 10 |
11 |12 | 推荐参考其中oauth/oauth_client.py文件即可。 13 |
14 |15 | 也可以参考我博客:第三方登录整理 16 |
17 |18 | 在我博客中,也对QQ、Sina、Github的OAuth开发过程中逐个写了博文: 19 |
20 |21 | 1、QQ第三方登录 22 |
23 |24 | 2、新浪微博第三方登录 25 |
26 |27 | 3、Github第三方登录 28 |
29 |33 | oauth是实现oauth主要的代码,templates是相关的模版文件(根据自己情况需要修改) 34 |
35 |36 | oauth相关设置记录在数据库中,即可以查看oauth/models.py中的OAuth_type设计。 37 |
38 |42 | 1、复制该应用到你的django项目中。 43 |
44 |45 | 2、打开settings.py文件,INSTALLED_APP中添加应用 'oauth' 46 |
47 |48 | 3、打开总的urls.py文件,添加本应用的路由设置 49 |
url(r'^oauth/',include('oauth.urls')),50 | 51 |
52 | 4、更新数据库 53 |
54 |python manage.py makemigrations 55 | python manage.py migrate56 |
57 | 5、进入django后台管理,新增OAuth设置(包括回调地址、请求链接等等) 58 |
59 |60 | 6、测试代码 --> 调试 --> 上线 61 |
-------------------------------------------------------------------------------- /oauth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaddyYang/django-oauth/6a9347dfb4a15465b77e9c2575ae8a8b7db13976/oauth/__init__.py -------------------------------------------------------------------------------- /oauth/admin.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | from django.contrib import admin 3 | from .models import OAuth_type, OAuth_ex 4 | 5 | # Register your models here. 6 | class OAuthTypeAdmin(admin.ModelAdmin): 7 | list_display=('id','type_name', 'title', 'img') 8 | 9 | #分组表单 10 | fieldsets = ( 11 | (u'OAuth类型信息', { 12 | "fields":('type_name', 'title', 'img') 13 | }), 14 | (u'OAuth基本设置', { 15 | "fields":('client_id','client_secret','redirect_uri','scope') 16 | }), 17 | (u'OAuth请求链接', { 18 | "fields":('url_authorize','url_access_token','url_open_id','url_user_info','url_email') 19 | }) 20 | ) 21 | 22 | class OAuthAdmin(admin.ModelAdmin): 23 | list_display=('id', 'user', 'openid','oauth_type') 24 | 25 | admin.site.register(OAuth_ex, OAuthAdmin) 26 | admin.site.register(OAuth_type, OAuthTypeAdmin) 27 | -------------------------------------------------------------------------------- /oauth/forms.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | from django import forms 3 | from django.core.exceptions import ValidationError 4 | from django.contrib.auth.models import User 5 | from django.contrib.auth import authenticate 6 | 7 | from .models import OAuth_ex, OAuth_type 8 | 9 | class BindEmail(forms.Form): 10 | """bind the openid to email""" 11 | openid = forms.CharField(widget=forms.HiddenInput(attrs={'id':'openid'})) 12 | nickname = forms.CharField(widget=forms.HiddenInput(attrs={'id':'nickname'})) 13 | oauth_type_id = forms.CharField(widget=forms.HiddenInput(attrs={'id':'oauth_type'})) 14 | 15 | email = forms.EmailField(label=u'注册邮箱', 16 | widget=forms.EmailInput(attrs={'class':'form-control', 'id':'email','placeholder':u'请输入您注册用的邮箱'})) 17 | pwd = forms.CharField(label=u'用户密码', max_length=36, 18 | widget=forms.PasswordInput(attrs={'class':'form-control', 'id':'pwd','placeholder':u'若尚未注册过,该密码则作为用户密码'})) 19 | 20 | #验证邮箱 21 | def clean_email(self): 22 | oauth_type_id = self.cleaned_data.get('oauth_type_id') 23 | email = self.cleaned_data.get('email') 24 | 25 | users = User.objects.filter(email = email) 26 | oauth_type = OAuth_type.objects.get(id = oauth_type_id) 27 | 28 | if users: 29 | #判断是否被绑定了 30 | if OAuth_ex.objects.filter(user = users[0], oauth_type = oauth_type): 31 | raise ValidationError(u'该邮箱已经被绑定了') 32 | return email 33 | 34 | #验证密码 35 | def clean_pwd(self): 36 | email = self.cleaned_data.get('email') 37 | pwd = self.cleaned_data.get('pwd') 38 | 39 | users = User.objects.filter(email = email) 40 | if users: 41 | #若用户存在,判断密码是否正确 42 | user = authenticate(username=email, password=pwd) 43 | if user is not None: 44 | return pwd 45 | else: 46 | return ValidationError(u'密码不正确,不能绑定') 47 | -------------------------------------------------------------------------------- /oauth/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaddyYang/django-oauth/6a9347dfb4a15465b77e9c2575ae8a8b7db13976/oauth/migrations/__init__.py -------------------------------------------------------------------------------- /oauth/migrations/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaddyYang/django-oauth/6a9347dfb4a15465b77e9c2575ae8a8b7db13976/oauth/migrations/__init__.pyc -------------------------------------------------------------------------------- /oauth/models.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | from django.db import models 3 | 4 | #若不是使用系统的用户认证,可以换成你自己的用户系统 5 | from django.contrib.auth.models import User 6 | 7 | class OAuth_type(models.Model): 8 | """OAuth类型""" 9 | type_name = models.CharField(max_length = 12) 10 | title = models.CharField(max_length = 12) 11 | #图片上传的路径可以修改成自己的 12 | img = models.FileField(upload_to='static/img/connect') 13 | 14 | #oauth基本设置 15 | client_id = models.CharField(max_length = 24, default='') 16 | client_secret = models.CharField(max_length = 48, default='') 17 | redirect_uri = models.URLField(default='') 18 | scope = models.CharField(max_length = 24, default='') 19 | 20 | #oauth请求链接 21 | url_authorize = models.URLField(default='', blank=True) 22 | url_access_token = models.URLField(default='', blank=True) 23 | url_open_id = models.URLField(default='', blank=True) 24 | url_user_info = models.URLField(default='', blank=True) 25 | url_email = models.URLField(default='', blank=True) 26 | 27 | def __unicode__(self): 28 | return self.type_name 29 | 30 | class OAuth_ex(models.Model): 31 | """User用户绑定""" 32 | user = models.ForeignKey(User) #和User关联的外键 33 | openid = models.CharField(max_length = 64) 34 | oauth_type = models.ForeignKey(OAuth_type, default=1) #关联账号的类型 35 | 36 | def __unicode__(self): 37 | return u'<%s>' % (self.user) 38 | -------------------------------------------------------------------------------- /oauth/oauth_client.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | import json 3 | import urllib, urllib2, urlparse 4 | import re 5 | 6 | class OAuth_Base(): 7 | def __init__(self, kw): 8 | if not isinstance(kw, dict): 9 | raise Exception("arg is not dict type") 10 | 11 | for key, value in kw.items(): 12 | setattr(self, key, value) 13 | 14 | @staticmethod 15 | def Get_OAth(**kw): 16 | """静态方法,根据参数实例化对应的类""" 17 | type_name = kw.get('type_name') 18 | 19 | if type_name == "QQ": 20 | oauth = OAuth_QQ(kw) 21 | elif type_name == "Sina": 22 | oauth = OAuth_Sina(kw) 23 | elif type_name == "Github": 24 | oauth = OAuth_Github(kw) 25 | else: 26 | oauth = None 27 | return oauth 28 | 29 | def _get(self, url, data): 30 | """get请求""" 31 | request_url = '%s?%s' % (url, urllib.urlencode(data)) 32 | response = urllib2.urlopen(request_url) 33 | return response.read() 34 | 35 | def _post(self, url, data): 36 | """post请求""" 37 | request = urllib2.Request(url, data = urllib.urlencode(data)) 38 | response = urllib2.urlopen(request) 39 | return response.read() 40 | 41 | #根据情况重写以下方法 42 | def get_auth_url(self): 43 | """获取授权页面的网址""" 44 | params = {'client_id': self.client_id, 45 | 'response_type': 'code', 46 | 'redirect_uri': self.redirect_uri, 47 | 'scope': self.scope, 48 | 'state': self.state} 49 | return '%s?%s' % (self.url_authorize, urllib.urlencode(params)) 50 | 51 | #继承的类要实现下面的方法 52 | def get_access_token(self, code): 53 | """根据code获取access_token""" 54 | pass 55 | 56 | def get_open_id(self): 57 | """获取用户的标识ID""" 58 | pass 59 | 60 | def get_user_info(self): 61 | """获取用户资料信息""" 62 | pass 63 | 64 | def get_email(self): 65 | """获取邮箱""" 66 | pass 67 | 68 | class OAuth_QQ(OAuth_Base): 69 | def get_access_token(self, code): 70 | """根据code获取access_token""" 71 | params = {'grant_type': 'authorization_code', 72 | 'client_id': self.client_id, 73 | 'client_secret': self.client_secret, 74 | 'code': code, 75 | 'redirect_uri': self.redirect_uri} 76 | response = self._get(self.url_access_token, params) 77 | 78 | #解析结果 79 | if response[:8] == 'callback': 80 | v_str = str(response)[9:-3] #去掉callback的字符 81 | v_json = json.loads(v_str) 82 | raise Exception(v_json['error_description']) 83 | else: 84 | result = urlparse.parse_qs(response, True) 85 | self.access_token = str(result['access_token'][0]) 86 | return self.access_token 87 | 88 | def get_open_id(self): 89 | """获取QQ的OpenID""" 90 | params = {'access_token': self.access_token} 91 | response = self._get(self.url_open_id, params) 92 | 93 | #去掉callback的字符 94 | result = json.loads(str(response)[9:-3] ) 95 | self.openid = result['openid'] 96 | return self.openid 97 | 98 | def get_user_info(self): 99 | """获取QQ用户的资料信息""" 100 | params = {'access_token': self.access_token, 101 | 'oauth_consumer_key': self.client_id, 102 | 'openid': self.openid} 103 | response = self._get(self.url_user_info, params) 104 | return json.loads(response) 105 | 106 | def get_email(self): 107 | """获取邮箱""" 108 | #QQ没有提供获取邮箱的方法 109 | raise Exception('can not get email') 110 | 111 | class OAuth_Sina(OAuth_Base): 112 | def get_access_token(self, code): 113 | """根据code获取access_token""" 114 | params = {'grant_type': 'authorization_code', 115 | 'client_id': self.client_id, 116 | 'client_secret': self.client_secret, 117 | 'code': code, 118 | 'redirect_uri': self.redirect_uri} 119 | 120 | #新浪微博此处是POST请求,返回JSON 121 | response = self._post(self.url_access_token, params) 122 | result = json.loads(response) 123 | 124 | self.access_token = result["access_token"] 125 | self.openid = result['uid'] 126 | return self.access_token 127 | 128 | def get_open_id(self): 129 | """获取Sina的uid,由于登录时就获取了,直接返回即可""" 130 | return self.openid 131 | 132 | def get_user_info(self): 133 | """获取用户资料信息""" 134 | params = {'access_token': self.access_token, 135 | 'uid': self.openid} 136 | response = self._get(self.url_user_info, params) 137 | return json.loads(response) 138 | 139 | def get_email(self): 140 | """获取邮箱""" 141 | #高级接口,需要申请 142 | params = {"access_token": self.access_token} 143 | response = self._get(self.url_email, params) 144 | #return response 145 | 146 | #分析结果,获取邮箱成功返回是一个字典数组,而不成功则是一个字典 147 | result = json.loads(response) 148 | 149 | #判断返回数据的类型 150 | if isinstance(result, list): 151 | #获取并判断邮箱格式是否正确 152 | email = result[0].get('email') 153 | pattern = re.compile(r'^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$') 154 | match = pattern.match(email) 155 | 156 | if not match: 157 | raise Exception(u"email format error") 158 | return email 159 | 160 | elif isinstance(result, dict): 161 | raise Exception(result.get('error', 'get email happened error')) 162 | else: 163 | raise Exception('get email api error') 164 | 165 | class OAuth_Github(OAuth_Base): 166 | openid = '' 167 | 168 | def get_access_token(self, code): 169 | params = {'grant_type': 'authorization_code', 170 | 'client_id': self.client_id, 171 | 'client_secret': self.client_secret, 172 | 'code': code, 173 | 'redirect_uri': self.redirect_uri} 174 | 175 | #Github此处是POST请求 176 | response = self._post(self.url_access_token, params) 177 | 178 | #解析结果 179 | result = urlparse.parse_qs(response, True) 180 | self.access_token = result['access_token'][0] 181 | return self.access_token 182 | 183 | def get_open_id(self): 184 | """获取用户的标识ID""" 185 | if not self.openid: 186 | #若没有openid,则调用一下获取用户信息的方法 187 | self.get_user_info() 188 | 189 | return self.openid 190 | 191 | def get_user_info(self): 192 | """获取用户资料信息""" 193 | params = {'access_token': self.access_token,} 194 | response = self._get(self.url_user_info, params) 195 | 196 | result = json.loads(response) 197 | self.openid = result.get('id', '') 198 | return result 199 | 200 | def get_email(self): 201 | """获取邮箱""" 202 | params = {'access_token': self.access_token,} 203 | response = self._get(self.url_email, params) 204 | 205 | result = json.loads(response) 206 | return result[0]['email'] 207 | -------------------------------------------------------------------------------- /oauth/urls.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | from django.conf.urls import include, url 3 | from .views as oauth_views 4 | 5 | #http://localhost:8000/oauth/ 6 | #start with 'oauth/' 7 | urlpatterns = [ 8 | url(r'^oauth_login/(?P{{form_tip|safe}}
13 | 37 |