之前用小程序做項目,因為后臺使用的java開發(fā),一切順利,但切換成django做RESTful API接口時,在登陸注冊時一直出現(xiàn)問題,網(wǎng)上搜索,借助一個網(wǎng)友的回答,找到了一種可行的解決方案,現(xiàn)記錄如下。
具體流程
- 用戶點擊小程序頁面上的登錄授權認證
- 通過微信自帶的認證獲取code
- 調(diào)取登錄接口,將code傳入后臺
- 后臺拿到code調(diào)用微信接口獲取openid等用戶信息
- 后臺將openid作為用戶名,若存在則去校驗用戶信息,否則以此用戶名創(chuàng)建新用戶,密碼隨機生成
- 將校驗結果或者創(chuàng)建信息返回給微信小程序端
- 根據(jù)返回的信息完成用戶登錄校驗
django的用戶權限認證
django有一套自己的完善用戶模型,由于Django Auth自帶的User模型字段有限,我們需要對其進行拓展(直接使用也可以)
nickname = models.CharField(verbose_name=u'昵稱',max_length=50, blank=True)
user_avatar = models.ImageField(verbose_name=u'用戶頭像', upload_to='image/%Y/%m/%d', default=u'image/default.png', max_length=500)
user_email = models.EmailField(verbose_name=u'用戶郵箱',max_length=254)
user_phone = models.BigIntegerField(verbose_name=u'手機號', null=True,blank=True)
user_birthday = models.DateField(verbose_name=u'出生日期', default = timezone.now)
user_sex = models.CharField(verbose_name=u'性別',max_length=6,choices=(('male','男'),('female','女')),default='male')
user_address = models.CharField(verbose_name=u'地址',max_length=550, blank=True,null=True)
signature = models.CharField(verbose_name=u'個性簽名',max_length=550, blank=True,null=True)
用戶接口序列化
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
登陸接口設計
class UserLogin(APIView):
def post(self,request):
params = request.data
userName = get_openid(params.get('code'))
userInfo = params.get('userinfo')
try:
user = User.objects.get(username = userName)
except Exception as e:
user = None
if user:
# 更新用戶信息
user = User.objects.get(username = userName)
else:
#注冊新用戶
user = User.objects.create_user(username=userName,password=random_str(10))
#手動生成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)
ret = {'code': '00000', 'msg': None,'data':{}}
ret['msg'] = '授權成功'
ret['data'] = {
'token': token,
'user_id': user.id,
'nickname': user.nickname
}
return JsonResponse(ret)
解析code獲取openid
class OpenidUtils(object):
def __init__(self, jscode):
self.url = "https://api.weixin.qq.com/sns/jscode2session"
self.appid = APPID
self.secret = SECRET
self.jscode = jscode # 前端傳回的動態(tài)jscode
def get_openid(self):
url = self.url + "?appid=" + self.appid + "&secret=" + self.secret + "&js_code=" + self.jscode + "&grant_type=authorization_code"
r = requests.get(url)
openid = r.json()['openid']
return openid
小程序的登陸驗證
具體登錄流程可以查閱官方文檔。
function getWXUserInfo() {
const login = promisify(wx.login);
const getUserInfo = promisify(wx.getUserInfo);
return new Promise(function (resolve, reject) {
_wxLogin();
function _wxLogin() {
login().then(function (res) {
getUserInfo().then(function (r) {
let userInfo = r;
userInfo.code = res.code;
try {
wx.setStorageSync('userInfo', userInfo);
} catch (e) {
console.log(e)
}
if (userInfo && userInfo.code && userInfo.iv) {
resolve(userInfo);
}
else {
reject('wx login fail');
}
}).catch(function (error) {
reject(error);
});
}).catch(function (error) {
reject(error);
});
}
});
}
//登錄接口驗證
getWXUserInfo().then(function (data) {
var result = {
code: 0,
data: {}
};
var params = {
'code':data.code,
'userinfo':data.userInfo
}
wx.request({
url: '/api/login',
data: params,
dataType: 'json',
method: 'POST',
success: function (response) {
// 返回成功
if (response.data && response.data.code == '00000') {
try {
var resData = {
custNo: data.user_id,
nickname: data.nickname
};
result.code = 0;
result.data = resData;
resolve(result);
}
catch (e) {
console.warn(result)
// 登錄失敗
result.code = 2;
resolve(result);
}
}
else {
// 獲取 customNum 失敗
console.warn(result)
result.code = 1;
result.data = 'get customNum fail';
resolve(result);
}
}
})
}