uiniapp实现微信授权登录
文章介绍最近开发了一个在线考试系统,因项目需求,需要集成微信授权登陆。在此总结一下整个授权登陆流程。本篇介绍包含前端和后端整个开发流程。业务需求每个用户都拥有学号,需进行学号及密码与微信授权的绑定。第一次登陆时,将用户的学号和微信号进行绑定。之后登录时,凡是进行微信绑定过的用户可以直接登陆,不需要再进行学号和密码的验证。登录成功后,昵称和头像,手机号都使用微信设置的。页面展示授权页面绑定账号页面我
文章介绍
最近开发了一个在线考试系统,因项目需求,需要集成微信授权登陆。在此总结一下整个授权登陆流程。本篇介绍包含前端和后端整个开发流程。
业务需求
每个用户都拥有学号,需进行学号及密码与微信授权的绑定。第一次登陆时,将用户的学号和微信号进行绑定。之后登录时,凡是进行微信绑定过的用户可以直接登陆,不需要再进行学号和密码的验证。登录成功后,昵称和头像,手机号都使用微信设置的。
页面展示
授权页面
绑定账号页面
我的页面
授权流程
- 调用wx.login获取code(服务端通过code等其他信息获取opendId,可以理解为微信存储我们微信号的唯一标识) 接口介绍
- 调用wx. getPhoneNumber获取手机号等信息 (该接口会返回iv和encryptedData,用于之后获取手机号解密) 接口介绍
- 调用wx.getUserProfile 获取用户的头像,昵称,地区等基本信息 接口介绍
- 用户输入需要绑定的账户信息
- 将以上四个步骤获取的信息,筛选后传递给后端服务。
- 后端服务获取用户的openid,进行用户的账号绑定。并且通过解析加密的手机号,获取真实的手机号以及通过getUserProfile 接口获取到的用户头像等基本信息,生成用户记录,存储到数据库中。
- 绑定成功后,返回token以及一些附属信息。
- 再次登陆时,直接通过获取用户的oppenid来判断用户是否已绑定。如果已绑定,则直接登陆。未绑定,重复上述步骤。
详细步骤
1.绑定用户账号
前端代码及流程
<button class=“bind-user” open-type=“getPhoneNumber” @getphonenumber=“getPhoneNumber”>绑 定 用 户
点击绑定用户,调用获取用户手机号的授权方法(写法固定)调用该方法必须绑定一个按钮。
注意:之后获取用户信息的接口也必须要绑定按钮(虽然不方便,但是是微信规定必须这样来的)
属性
data(){
return{
user_placeholder:'账号',
discribe:'所属机构账号登录',
//登陆表单信息
loginForm:{
username:'',
password:'',
code:'',
uuid:'',
},
//用户信息,调用wx.getUserProfile获取
userInfo:{
nickName:"",
gender:0,
language:"",
city:"",
province:"",
country:"",
avatarUrl:"",
},
//验证码
codeImg:'',
//部门信息
dept:[],
deptName:'',
deptId:0,
show:false,
//是否需要提供验证码
isVerify:false,
//判断表格数据是否完善
formComplete:false,
}
},
获取用户手机号和基本信息
//获取用户手机号
getPhoneNumber(e){
//e.detail 即wx.getPhoneNumber返回的用户信息
let phoneDetail = e.detail;
let _this = this;
//该方法是用于校验填写的部门,用户名和密码有没有空的。都不为空返回true,否则弹窗提示。
let isContinue=_this.judgeUserBinding();
if(isContinue===true){
if(e.detail.errMsg==="getPhoneNumber:ok"){
//获取手机号成功后,我们还需要获取用户的头像,昵称等基本信息。同样,获取这些信息还需要绑定按钮。
wx.showModal({
title: '提示',
content: '授权获取用户基本信息',
success (res) {
if (res.confirm) {
wx.getUserProfile({
desc: '获取微信用户基本信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
if(res.errMsg==="getUserProfile:ok"){
let userInfo = res.userInfo;
//存储用户的接口获取用户的基本信息
_this.userInfo=res.userInfo;
_this.getUserPhoneNumber(e);
}else{
console.log('用户拒绝用户信息授权')
}
},
fail: (res)=> {
console.log('wx.getUserProfile=>用户拒绝了授权');
console.log(res);
},
})
} else if (res.cancel) {
console.log('用户取消用户信息授权')
}
}
})
}else{
console.log('用户拒绝手机号授权')
}
}
},
数据处理并调用接口getUserPhoneNumber
getUserPhoneNumber(e){
let _this=this;
let phoneDetail = e.detail;
if(e.detail.errMsg==="getPhoneNumber:ok"){
//获取手机号成功,调用wx.login获取code
wx.login({
success:(res)=>{
wx.request({
url: baseUrl+"/weChat/weChatLogin",
data:{
//用户信息
username:_this.loginForm.username,
password:cryptFirst.encrypt(_this.loginForm.password),
verifyCode:_this.loginForm.code,
uuid:_this.loginForm.uuid,
deptId:_this.deptId,
//微信验证信息
code:res.code,
//手机号解密信息
iv:phoneDetail.iv,
encryptedData:phoneDetail.encryptedData ,
//微信用户信息
nickName:_this.userInfo.nickName,
gender:_this.userInfo.gender,
avatarUrl:_this.userInfo.avatarUrl,
},
//下面为成功或失败后的跳转
success:(res)=>{
if(res.data.code!=0){
wx.showToast({
title: res.data.message,
icon: 'error',
duration: 2000
})
}else{
console.log(res)
if(res.data.data.loginStatus===true){
//成功登陆
uni.setStorageSync("token",res.data.data.token)
//返回上一个页面
uni.navigateBack({
delta: 2
});
}else{
wx.showToast({
title: res.data.data.failReason,
icon: 'error',
duration: 2000
})
if(res.data.data.isVerify===true){
_this.isVerify=true;
//下次登陆需要验证码
_this.codeImg=res.data.data.img;
_this.loginForm.uuid = res.data.data.uuid;
}
}
}
},
fail:(res)=>{
}
})
}
})
}
},
后端代码及流程
- 通过前端提供的code,获取每个微信用户唯一的openid和sessionkey
- 检验该openid是否已绑定用户(如果已绑定,则可以直接登陆)注意:如果微信已绑定用户,在开启微信授权登录那个页面就已成功登录,不会再跳转到账号绑定页面。(一个账号不能绑定两个微信号,反之同理)
- 通过iv和encryptedData以及sessionkey,解密获取手机号
- 创建用户
- 返回token等其他业务需求。
根据code获取信息
String url = "https://api.weixin.qq.com/sns/jscode2session?" +
"appid=" + weChat.getAppId() +
"&secret=" + weChat.getSecret() +
"&js_code=" + weChatVo.getCode() +
"&grant_type=" + weChat.getGrantType();
JSONObject currentUserResult = sendRequest(url);
String openid = currentUserResult.getString("openid");
String sessionKey = currentUserResult.getString("session_key");
注意,除了code。appid,secret,grant_type都是需要注册小程序后,微信方生成的。可在小程序管理平台中生成查看
* @description: 发送请求
public JSONObject sendRequest(String url) {
HttpURLConnection conn = null;
try {
URL serverUrl = new URL(url);
conn = (HttpURLConnection) serverUrl.openConnection();
conn.setRequestMethod("GET");
//设置连接超时时间和读取超时时间
conn.setConnectTimeout(15000);
conn.setReadTimeout(60000);
conn.setRequestProperty("Accept", "application/json");
//必须设置false,否则会自动redirect到重定向后的地址
conn.setInstanceFollowRedirects(false);
//发送请求
conn.connect();
} catch (Exception e) {
e.printStackTrace();
}
//获取返回值
return getHttpReturn(conn);
}
//获取请求返回值
public JSONObject getHttpReturn(HttpURLConnection connection) {
//将返回的输入流转换成字符串
BufferedReader bufferedReader = null;
InputStreamReader inputStreamReader = null;
String result = "";
try {
if (200 == connection.getResponseCode()) {
StringBuffer buffer = new StringBuffer();
inputStreamReader = new InputStreamReader(connection.getInputStream(), "UTF-8");
bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
result = buffer.toString();
} else {
System.out.println("ResponseCode is an error code:" + connection.getResponseCode());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
connection.disconnect();
}
JSONObject res = JSONObject.parseObject(result);
return res;
}
检测用户是否绑定oppenid
查库判断即可
获取手机号
//获取手机号
String phoneNumber = decrypt(weChatVo.getEncryptedData(), weChatVo.getIv(), sessionKey);
public String decrypt(String encryptedData, String iv, String sessionKey) {
byte[] encryptedDataDecode = Base64.getDecoder().decode(encryptedData);
byte[] sessionKeyDecode = Base64.getDecoder().decode(sessionKey);
byte[] ivDecode = Base64.getDecoder().decode(iv);
AES aes = new AES(Mode.CBC, Padding.NoPadding, sessionKeyDecode, ivDecode);
byte[] decrypt = aes.decrypt(encryptedDataDecode);
String stringData = new String(decrypt);
JSONObject jsonObject = JSONObject.parseObject(stringData);
return String.valueOf(jsonObject.get("phoneNumber"));
}
2.已绑定用户直接授权登陆
大体流程和上方流程差不多,后端代码 完全一样,这里主要贴一下前端代码。
<template>
<view class="head-body">
<view class="pic-button">
<view class="logo">
<image src="../../static/login/logo144.png" mode="" ></image>
</view>
<p>校园云安全</p>
<view class="authorize">
<button class="button" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">开启授权登录</button>
</view>
</view>
</view>
</template>
<script>
import baseUrl from '@/utils/env.js';
export default{
data(){
return{
isBindUser:false
}
},
methods:{
//获取用户手机号
getPhoneNumber(e){
let _this = this;
_this.getUserPhoneNumber(e);
},
getUserPhoneNumber(e){
let _this=this;
if(e.detail.errMsg==="getPhoneNumber:ok"){
//获取手机号成功
wx.login({
success:(res)=>{
wx.request({
url: baseUrl+"/weChat/weChatLogin",
data:{
code:res.code,
},
success:(res)=>{
if(res.data.code!=0){
wx.showToast({
title: res.data.message,
icon: 'error',
duration: 2000
})
}else{
//授权成功
if(res.data.data.isBindUser===true){
//需要绑定用户
_this.isBindUser=true;
//跳转页面
_this.toBingUser();
}else{
//用户绑定成功并且成功登陆
uni.setStorageSync("token",res.data.data.token)
//返回上一个页面
uni.navigateBack({
delta: 1
});
}
}
},
fail:(res)=>{
wx.showToast({
title: "绑定失败,请重试",
icon: 'error',
duration: 2000
})
}
})
}
})
}else{
console.log("用户拒绝授权");
}
},
toBingUser(){
wx.showModal({
title: '提示',
content: '请绑定用户信息',
success (res) {
if (res.confirm) {
uni.navigateTo({
url:"../login/login"
})
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
},
}
}
</script>
总结
凡尔赛一下,虽然代码挺多,但是整体挺简单的。。。
刚开始写这个业务时,走得坑挺多的。网上看了许多博客,但是总是不知道该如何下手,官方文档看着又不是很清晰,又踩了许多坑,希望这篇文章,能够帮助大家快速上手。
最后附一张别人BLOG中的图片,这张图片是真的牛批!!!但是这张图片的提到的获取用户信息和手机号的方法已经被淘汰了,只做一个流程的参考使用。
更多推荐
所有评论(0)