最近在弄小程序登录模块,看了微信官方文档,还找了挺多博客,也不知道那个登录态怎么弄。话说这个session,真的不是很懂。由于openid几乎是不变的(同一个appID下),于是就采用openid来请求数据,然后自己自己研究了一套登陆逻辑,不采用session请求,每次传输请求带一个openid过去操作吧,这样就简单多了。
下图是我的画的登录逻辑流程(有点丑,大家将就看吧):
登录流程:
1、用户第一次登录,先通过wx.getSetting判断小程序是否授权过,此时是没有授权过的,于是进入授权页面调用登录API wx.login获取到一个code,将此code连同用户信息userInfo一并发到开发者服务器。
2、开发者服务器获取到code之后,连同appID、appSecret通过网络请求微信服务器的code2session接口,获取到openid和session_key。
3、将步骤2的openid拿到数据库中查找是否有该用户数据,没有的话就将信息插入到数据库。我在此把openid、session_key(虽然没用到这个数据,还是先预留着,万一下次需要用到呢)、userInfo整理存入数据库,然后返回一个openid给小程序;如果数据库中有该条信息,则返回一个openid给小程序。
4、小程序获取到服务器返回的openid后,保存到本地缓存中,并放入全局变量,跳转到用户主界面。
5、小程序第二次登录,通过wx.getSetting,可以查到此时是用户授权过的,然后就同步获取缓存中的openid。若获取失败和没有找到openid的话,就再次调用wx.login登录获取openid,之后步骤也和2、3一样;若获取到了openid,就把他放到全局变量中,直接跳转主页面,不再请求开发者服务器。
下面来看看代码吧:
页面就直接贴出来了:
首先是app.js的逻辑,用于获取授权状态判断
// 封装的登陆逻辑
var loginUtil = require("/utils/login.js")
App({
onLaunch: function () {
// 获取用户信息
wx.getSetting({
success: res => {
console.log("wx.getSetting判断授权情况",res)
if (res.authSetting['scope.userInfo']) {
// 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
wx.getUserInfo({
success: res => {
// 将用户信息放入到全局变量
this.globalData.userInfo = res.userInfo
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
if (this.userInfoReadyCallback) {
this.userInfoReadyCallback(res)
}
// 查看缓存的openid
// 若没有则重新登录获取;存在的话放入全局变量
try{
let openid = wx.getStorageSync('openid')
if(openid != null && openid != ""){
console.log("获取到openid、放入globalData中")
getApp().globalData.openid = openid
// 跳转主页面
wx.redirectTo({
url: "/main/index/index"
})
}else{
console.log("openid为空,重新登录获取")
loginUtil.login()
}
}catch(err){
console.log("openid获取异常,重新登录获取")
loginUtil.login()
}
}
})
}
}
})
},
globalData: {
userInfo: null,
openid: null
}
})
登陆请求我是单独写成一个工具的,通过wx.login获取到code,然后连同userInfo上传到开发者服务器:
function login(){
console.log("登陆请求获取openid")
// 登录
wx.login({
success: res => {
console.log("wx.login.code", res)
// 加载进度
wx.showLoading({
title: '正在请求数据',
mask: false
})
// 发送 res.code 到后台换取 openId, sessionKey,返回openid保存
wx.request({
url: 'http://192.168.0.240:8080/MiniProgramServer/login/login',
data: {
code: res.code,
// 将用户信息传到服务器保存
userInfo: getApp().globalData.userInfo
},
header: {
'content-type': 'application/json' // 默认值
},
method: 'POST',
success(res) {
console.log("成功", res)
// 获取到openid后将openid存入本地,同时放到全局变量里
getApp().globalData.openid = res.data.openid
wx.setStorage({
key: 'openid',
data: res.data.openid
})
wx.hideLoading()
// 然后跳转主页面
wx.redirectTo({
url: '/main/index/index'
})
},
fail(res){
console.log("请求失败")
wx.hideLoading()
wx.showToast({
title: '请求失败',
icon: 'none', // "success", "loading", "none"
duration: 1500,
mask: false
})
}
})
// request请求结束
}
})
};
module.exports = {
login: login
}
登录界面,主要是添加一个授权的按钮,页面弄得不咋的,见谅啊,页面地址是 global/login/index
<!-- 这是登录页面 global/login/index -->
<image class="img" src="/assets/img/wechat.png" mode="scaleToFill"></image>
<view class="line"></view>
<view class="title">申请获取以下权限</view>
<view class="content">获取你的公开信息(昵称、头像等)</view>
<button class="btn" type="primary" open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">授权登录</button>
登录界面样式
/* 这是登录页面样式 global/login/index */
page{
width: 100%;
height:100%;
display:flex;
flex-direction:column;
justify-content: flex-start;
align-items:center;
}
.img{
width: 180rpx;
height: 180rpx;
background-size: cover;
margin-top: 300rpx;
}
.line{
width: 600rpx;
height: 2rpx;
background-color: grey;
margin: 50rpx 0 30rpx 0;
}
.title{
width: 500rpx;
height: 70rpx;
font-size: 35rpx;
}
.content{
width: 500rpx;
height: 70rpx;
font-size: 30rpx;
color: grey;
}
.btn{
width: 600rpx;
height: 70rpx;
font-size: 30rpx;
line-height: 70rpx;
color: #FFF;
background-color: #2DC100;
border-radius: 70rpx;
}
登录界面的处理逻辑,主要处理授权:
// 封装的登录模块
let loginUtil = require("../../utils/login.js")
Page({
/**
* 页面的初始数据
*/
data: {
//判断小程序的API,回调,参数,组件等是否在当前版本可用
canIUse: wx.canIUse("button.open-type=getUserInfo")
},
// 用户授权
bindGetUserInfo: (res) => {
console.log("bindGetUserInfo获取权限",res)
if(res.detail.userInfo){
// 成功获取到权限,将用户信息存入全局变量
console.log("bindGetUserInfo获取用户信息成功!")
getApp().globalData.userInfo = res.detail.userInfo
// 登录
loginUtil.login()
}else{
console.log("bindGetUserInfo获取用户信息失败!")
wx.showToast({
title: '您想正常使用,请先授权哦!',
icon: 'none', // "success", "loading", "none"
duration: 2000,
mask: false
})
}
}
})
跳转的主页面,这里我就偷懒了,就写了一行代码(简单点写吧),页面地址 main/index/index
<text style="color:pink;font-size:40rpx;margin-top:300rpx;">欢迎进入!</text>
后台服务器:
服务器端使用的是自己之前写的一个SSM框架的demo,jar包使用的是maven管理,不习惯maven的,我还在demo的libs下面提取出了所需的jar包,大家可以直接创建web工程,将jar包扔进去,再配一下web.xml就行啦。我使用这个这个SSM的demo做服务器的原因有两点,一嘛就是有点懒不想动,二来则是直接通过注解就能将一些对象装换成JSON,挺方便的(其实还是懒啊)。
也是直接扔代码吧:
登陆逻辑:
@ResponseBody
@RequestMapping("/login")
public Object login(@RequestBody Map<String,Object> obj,HttpServletRequest request) {
// 获取服务器传送过来的数据
String key = (String) obj.get("code");
System.err.println(key);
Map<String,Object> userInfo = (Map<String,Object>)obj.get("userInfo");
System.out.println(userInfo);
//微信服务器返回的openid和session_key数据
String res = request(key);
// 解析数据
String[] wxres = res.replace("\"", "").split(",");
String openid = wxres[1].split(":")[1].replace("}", "");
String session_key = wxres[0].split(":")[1];
// 查找数据库是否有该用户,没有则新增,有的话不做处理
int count = select(openid);
if(count < 1) {
//插入数据库
System.out.println("新用户,插入数据库");
insert(openid,session_key,userInfo);
}else {
System.out.println("老用户,返回openid");
}
//整理数据,向客户端返回openid
return "{\"openid\":\""+openid+"\"}";
}
请求code2session接口:
/**
* 发起网络请求
* @param code 小程序发过来的code
* @return 微信服务器返回的数据
*/
public String request(String code) {
try {
String str = "https://api.weixin.qq.com/sns/jscode2session?";
str += "appid=你的小程序的appId";
str += "&secret=你的小程序的appSecret";
str += "&js_code="+code+"&grant_type=authorization_code";
URL url = new URL(str);
// 请求微信服务器
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(5000);
conn.setRequestMethod("POST");
if(conn.getResponseCode() == 200) {
//用getInputStream()方法获得服务器返回的输入流
InputStream in = conn.getInputStream();
// 获取服务器返回数据,将数据转成字符串
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
int rc = 0;
while ((rc = in.read(buff, 0, 100)) > 0) {
swapStream.write(buff, 0, rc);
}
byte[] byteData = swapStream.toByteArray();
String data = new String(byteData, "UTF-8");
System.out.println("成功获取微信服务器返回的数据"+data);
in.close();
return data;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
插入数据库(使用的是原生jdbc,想简单点但不会写了):
//存入数据库
public static int insert(String openid, String session_key, Map<String,Object> userInfo) {
String url = "jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf-8";
String username = "root";
String passwd = "root";
try {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//连接数据库
Connection conn = (Connection) DriverManager.getConnection(url, username, passwd);
//操作数据库
Statement st = (Statement) conn.createStatement();
String sql = "insert into wx_login(session_key,openid,nickName,gender,city,province,country,avatarUrl,addTime) values('"
+session_key+"','"+openid+"','"+userInfo.get("nickName")+"',"+userInfo.get("gender")+",'"+userInfo.get("city")
+"','"+userInfo.get("province")+"','"+userInfo.get("country")+"','"+userInfo.get("avatarUrl")+"','"
+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date())+"')";
int count = st.executeUpdate(sql);
System.out.println("插入成功返回影响条数:"+count);
if(count > 0) {
return count;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
根据openid查询数据库是否有该用户:
/**
* 通过openid查找是否有该用户
* @param openid
* @return
*/
public static int select(String openid) {
String url = "jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf-8";
String username = "root";
String passwd = "root";
int count = 0;
try {
Class.forName("com.mysql.jdbc.Driver");
// 创建连接
Connection conn = (Connection) DriverManager.getConnection(url, username, passwd);
// 操作数据库
Statement st = (Statement) conn.createStatement();
String sql = "select count(1) from wx_login where openid = '"+openid+"'";
ResultSet rs = st.executeQuery(sql);
while(rs.next()) {
count = rs.getInt(1);//太久没用原生jdbc了,这里的columnIndex默认是从1开始的
}
System.out.println("查找到该用户的数据条数:"+count);
return count;
}catch(Exception e) {
e.printStackTrace();
}
return 0;
}
数据库脚本如下:
CREATE DATABASE ;
USE `ssm`;
DROP TABLE IF EXISTS `wx_login`;
CREATE TABLE `wx_login` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`openid` varchar(255) DEFAULT NULL,
`session_key` varchar(255) DEFAULT NULL,
`nickName` varchar(255) DEFAULT NULL,
`gender` int(1) DEFAULT NULL,
`country` varchar(255) DEFAULT NULL,
`province` varchar(255) DEFAULT NULL,
`city` varchar(255) DEFAULT NULL,
`avatarUrl` varchar(255) DEFAULT NULL,
`addTime` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;
insert into `wx_login`(`id`,`openid`,`session_key`,`nickName`,`gender`,`country`,`province`,`city`,`avatarUrl`,`addTime`) values
(32,'oyMvT5Lihfbj04qU6EVanyq3g9Fo','d4fYu4h42whrYXy3z/keow==','林哥哥',1,'China','Sichuan','Luzhou','https://wx.qlogo.cn/mmopen/vi_32/X9FT5ck3rC9vZ8vkwfQibzicuhyicYaDMuyzrpAr5ItRKfUXCLmIHIw9vTaJGdWroMMUhUFrKD60rtlicwmNOSbB9w/1322','2019-01-20 04:33:35')
大致就是这样的,有问题可以留言啊,demo上传到Github了,地址: https://github.com/stevenlin5520/LoginDemo