- 方便快捷:微信授权登录可以让用户使用自己的微信账号轻松登录小程序,省去了繁琐的注册流程,提升了用户的登录体验。
- 用户信任:微信是广泛使用的社交平台之一,在用户心中有一定的信任度,使用微信授权登录可以使用户更容易接受和信任小程序。
- 用户信息获取:通过微信授权登录,小程序可以获得用户的基本信息,如昵称、头像、性别等,方便个性化化用户的体验和提供更加精准的服务。
- 用户选择限制:使用微信授权登录的小程序,只有微信用户才能登录和使用,这样限制了其他非微信用户的使用可能性。
- 隐私问题:使用微信授权登录需要获取用户的基本信息,这可能引发用户的隐私担忧。如果小程序没有良好的隐私政策和数据保护机制,用户可能不愿意进行授权登录。
- 平台依赖性:小程序依赖于微信平台,如果微信平台发生故障或受到限制,可能会影响小程序的正常运行。
我们可以参考官网开放能力 / 用户信息 / 小程序登录 (qq.com)来进行一个理解
- 调用wx.login()获取 临时登录凭证code ,并回传到开发者服务器。
- 调用auth.code2Session接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台账号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台账号) 和 会话密钥 session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
wx.login是用于获取用户登录凭证code的接口,通过调用wx.login可以获取到用户的临时登录凭证code,用于后续换取用户唯一标识openid和会话密钥session_key,以实现用户登录功能。
wxml
昵称:{{userInfo.nickName}}
js
// pages/index/index.js
Page({
data: {
userInfo: {}
},
onLoad() {
}
wxLogin: function(e) {
debugger
console.log('wxLogin')
console.log(e.detail.userInfo);
this.setData({
userInfo: e.detail.userInfo
})
if (e.detail.userInfo == undefined) {
app.globalData.hasLogin = false;
util.showErrorToast('微信登录失败');
return;
}
}
})
wx.getUserProfile是用于获取用户个人信息的接口,通过调用wx.getUserProfile可以获取到用户的昵称、头像、性别等个人信息。
wxml
昵称:{{userInfo.nickName}}
js
// pages/index/index.js
Page({
data: {
userInfo: {}
},
wxLogin: function(e) {
debugger
console.log('wxLogin')
console.log(e.detail.userInfo);
this.setData({
userInfo: e.detail.userInfo
})
if (e.detail.userInfo == undefined) {
app.globalData.hasLogin = false;
util.showErrorToast('微信登录失败');
return;
}
}
})
wx.login和wx.getUserProfile是微信小程序中用于用户登录和获取用户信息的两个接口,wx.login用于实现用户登录功能,而wx.getUserProfile用于获取用户的个人信息。这两者常常一起使用,通过调用wx.login获取用户的登录凭证code,然后再调用wx.getUserProfile获取用户的个人信息,从而实现更完整的用户认证与信息收集。
它们有以下区别:
功能不同:
- wx.login:wx.login是用于获取用户登录凭证code的接口,通过调用wx.login可以获取到用户的临时登录凭证code,用于后续换取用户唯一标识openid和会话密钥session_key,以实现用户登录功能。
- wx.getUserProfile:wx.getUserProfile是用于获取用户个人信息的接口,通过调用wx.getUserProfile可以获取到用户的昵称、头像、性别等个人信息。
调用方式不同:
- wx.login:wx.login是基础库提供的原生接口,可以直接调用。
- wx.getUserProfile:wx.getUserProfile是在基础库2.10.4版本引入的,需要在小程序后台配置中开启相应权限,并在用户首次授权后才能获取到用户的个人信息。
返回值类型不同:
- wx.login:wx.login接口返回的是一个包含用户登录凭证code的对象。
- wx.getUserProfile:wx.getUserProfile接口返回的是一个包含用户个人信息的对象,包括昵称、头像、性别等。
用来判断是否用户已经登录了
package com.zking.ssm.wxcontroller; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import java.util.Random; import com.zking.ssm.annotation.LoginUser; import com.zking.ssm.model.WxUser; import com.zking.ssm.service.UserTokenManager; import com.zking.ssm.service.WxUserService; import com.zking.ssm.util.ResponseUtil; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; /** * 用户服务 */ @Slf4j @RestController @RequestMapping("/wx/user") @Validated public class WxUserController { @Autowired private WxUserService userService; /** * 用户个人页面数据 *
* @param userId * 用户ID * @return 用户个人页面数据 */ @GetMapping("index") public Object list(@LoginUser Integer userId, @RequestHeader("X-OA-token") String token) { log.info("【请求开始】用户个人页面数据,请求参数,userId:{}", userId); log.info("【请求开始】用户个人页面数据,请求参数,token:{}", token); if (userId == null) { log.error("用户个人页面数据查询失败:用户未登录!!!"); return ResponseUtil.unlogin(); } WxUser wxUser = userService.selectByPrimaryKey(userId); Map
用来实现功能的方法
package com.zking.ssm.wxcontroller; /** * @Autho donkee * @Since 2022/6/27 */ import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import com.alibaba.fastjson.JSONObject; import com.zking.ssm.annotation.LoginUser; import com.zking.ssm.model.UserInfo; import com.zking.ssm.model.WxLoginInfo; import com.zking.ssm.model.WxUser; import com.zking.ssm.service.UserToken; import com.zking.ssm.service.UserTokenManager; import com.zking.ssm.service.WxUserService; import com.zking.ssm.util.JacksonUtil; import com.zking.ssm.util.ResponseUtil; import com.zking.ssm.util.UserTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; import javax.servlet.http.HttpServletRequest; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 鉴权服务 */ @Slf4j @RestController @RequestMapping("/wx/auth") public class WxAuthController { @Autowired private WxMaService wxService; @Autowired private WxUserService userService; /** * 微信登录 * * @param wxLoginInfo * 请求内容,{ code: xxx, userInfo: xxx } * @param request * 请求对象 * @return 登录结果 */ @PostMapping("login_by_weixin") public Object loginByWeixin(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) { //客户端需携带code与userInfo信息 String code = wxLoginInfo.getCode(); UserInfo userInfo = wxLoginInfo.getUserInfo(); if (code == null || userInfo == null) { return ResponseUtil.badArgument(); } //调用微信sdk获取openId及sessionKey String sessionKey = null; String openId = null; try { long beginTime = System.currentTimeMillis(); // WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code); // Thread.sleep(6000); long endTime = System.currentTimeMillis(); log.info("响应时间:{}",(endTime-beginTime)); sessionKey = result.getSessionKey();//session id openId = result.getOpenid();//用户唯一标识 OpenID } catch (Exception e) { e.printStackTrace(); } if (sessionKey == null || openId == null) { log.error("微信登录,调用官方接口失败:{}", code); return ResponseUtil.fail(); }else{ log.info("openId={},sessionKey={}",openId,sessionKey); } //根据openId查询wx_user表 //如果不存在,初始化wx_user,并保存到数据库中 //如果存在,更新最后登录时间 WxUser user = userService.queryByOid(openId); if (user == null) { user = new WxUser(); user.setUsername(openId); user.setPassword(openId); user.setWeixinOpenid(openId); user.setAvatar(userInfo.getAvatarUrl()); user.setNickname(userInfo.getNickName()); user.setGender(userInfo.getGender()); user.setUserLevel((byte) 0); user.setStatus((byte) 0); user.setLastLoginTime(new Date()); user.setLastLoginIp(IpUtil.client(request)); user.setShareUserId(1); userService.add(user); } else { user.setLastLoginTime(new Date()); user.setLastLoginIp(IpUtil.client(request)); if (userService.updateById(user) == 0) { log.error("修改失败:{}", user); return ResponseUtil.updatedDataFailed(); } } // token UserToken userToken = null; try { userToken = UserTokenManager.generateToken(user.getId()); } catch (Exception e) { log.error("微信登录失败,生成token失败:{}", user.getId()); e.printStackTrace(); return ResponseUtil.fail(); } userToken.setSessionKey(sessionKey); log.info("SessionKey={}",UserTokenManager.getSessionKey(user.getId())); Map
result = new HashMap (); result.put("token", userToken.getToken()); result.put("tokenExpire", userToken.getExpireTime().toString()); userInfo.setUserId(user.getId()); if (!StringUtils.isEmpty(user.getMobile())) {// 手机号存在则设置 userInfo.setPhone(user.getMobile()); } try { DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); String registerDate = df.format(user.getAddTime() != null ? user.getAddTime() : new Date()); userInfo.setRegisterDate(registerDate); userInfo.setStatus(user.getStatus()); userInfo.setUserLevel(user.getUserLevel());// 用户层级 userInfo.setUserLevelDesc(UserTypeEnum.getInstance(user.getUserLevel()).getDesc());// 用户层级描述 } catch (Exception e) { log.error("微信登录:设置用户指定信息出错:"+e.getMessage()); e.printStackTrace(); } result.put("userInfo", userInfo); log.info("【请求结束】微信登录,响应结果:{}", JSONObject.toJSONString(result)); return ResponseUtil.ok(result); } /** * 绑定手机号码 * * @param userId * @param body * @return */ @PostMapping("bindPhone") public Object bindPhone(@LoginUser Integer userId, @RequestBody String body) { log.info("【请求开始】绑定手机号码,请求参数,body:{}", body); String sessionKey = UserTokenManager.getSessionKey(userId); String encryptedData = JacksonUtil.parseString(body, "encryptedData"); String iv = JacksonUtil.parseString(body, "iv"); WxMaPhoneNumberInfo phoneNumberInfo = null; try { phoneNumberInfo = this.wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv); } catch (Exception e) { log.error("绑定手机号码失败,获取微信绑定的手机号码出错:{}", body); e.printStackTrace(); return ResponseUtil.fail(); } String phone = phoneNumberInfo.getPhoneNumber(); WxUser user = userService.selectByPrimaryKey(userId); user.setMobile(phone); if (userService.updateById(user) == 0) { log.error("绑定手机号码,更新用户信息出错,id:{}", user.getId()); return ResponseUtil.updatedDataFailed(); } Map data = new HashMap (); data.put("phone", phone); log.info("【请求结束】绑定手机号码,响应结果:{}", JSONObject.toJSONString(data)); return ResponseUtil.ok(data); } /** * 注销登录 */ @PostMapping("logout") public Object logout(@LoginUser Integer userId) { log.info("【请求开始】注销登录,请求参数,userId:{}", userId); if (userId == null) { return ResponseUtil.unlogin(); } try { UserTokenManager.removeToken(userId); } catch (Exception e) { log.error("注销登录出错:userId:{}", userId); e.printStackTrace(); return ResponseUtil.fail(); } log.info("【请求结束】注销登录成功!"); return ResponseUtil.ok(); } }
精准内容以官方文档为准微信开放文档。
wx.getUserProfile登录的方法,发送到后端,利用私钥进行操作保存
var util = require('../../../utils/util.js'); var user = require('../../../utils/user.js'); const app = getApp(); getUserProfile(e) { // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认 // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 wx.getUserProfile({ desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 success: (res) => { //console.log(res); // debugger user.checkLogin().catch(() => { user.loginByWeixin(res.userInfo).then(res => { app.globalData.hasLogin = true; // debugger wx.navigateBack({ delta: 1 }) }).catch((err) => { app.globalData.hasLogin = false; if(err.errMsg=="request:fail timeout"){ util.showErrorToast('微信登录超时'); }else{ util.showErrorToast('微信登录失败'); } this.setData({ lock:false }) }); }); }, fail: (res) => { app.globalData.hasLogin = false; console.log(res); util.showErrorToast('微信登录失败'); } }); }, onLoad: function(options) { // 页面初始化 options为页面跳转所带来的参数 // 页面渲染完成 if (wx.getUserProfile) { this.setData({ canIUseGetUserProfile: true }) } //console.log('login.onLoad.canIUseGetUserProfile='+this.data.canIUseGetUserProfile) }
完整代码
// pages/auth/login/login.js var util = require('../../../utils/util.js'); var user = require('../../../utils/user.js'); const app = getApp(); Page({ /** * 页面的初始数据 */ data: { canIUseGetUserProfile: false, // 用于向前兼容 lock:false }, onLoad: function(options) { // 页面初始化 options为页面跳转所带来的参数 // 页面渲染完成 if (wx.getUserProfile) { this.setData({ canIUseGetUserProfile: true }) } //console.log('login.onLoad.canIUseGetUserProfile='+this.data.canIUseGetUserProfile) }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { }, /** * 生命周期函数--监听页面显示 */ onShow() { }, getUserProfile(e) { // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认 // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 wx.getUserProfile({ desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 success: (res) => { //console.log(res); // debugger user.checkLogin().catch(() => { user.loginByWeixin(res.userInfo).then(res => { app.globalData.hasLogin = true; // debugger wx.navigateBack({ delta: 1 }) }).catch((err) => { app.globalData.hasLogin = false; if(err.errMsg=="request:fail timeout"){ util.showErrorToast('微信登录超时'); }else{ util.showErrorToast('微信登录失败'); } this.setData({ lock:false }) }); }); }, fail: (res) => { app.globalData.hasLogin = false; console.log(res); util.showErrorToast('微信登录失败'); } }); }, wxLogin: function(e) { if (e.detail.userInfo == undefined) { app.globalData.hasLogin = false; util.showErrorToast('微信登录失败'); return; } user.checkLogin().catch(() => { user.loginByWeixin(e.detail.userInfo).then(res => { app.globalData.hasLogin = true; wx.navigateBack({ delta: 1 }) }).catch((err) => { app.globalData.hasLogin = false; if(err.errMsg=="request:fail timeout"){ util.showErrorToast('微信登录超时'); }else{ util.showErrorToast('微信登录失败'); } }); }); }, accountLogin() { console.log('开发中....') } })
用户获取信息
页面
获取用户信息
onShow: function () { let that = this; //获取用户的登录信息 let userInfo = wx.getStorageSync('userInfo'); this.setData({ userInfo: userInfo, hasLogin: true }); }
完整代码
var util = require('../../../utils/util.js'); var api = require('../../../config/api.js'); var user = require('../../../utils/user.js'); var app = getApp(); Page({ /** * 页面的初始数据 */ data: { userInfo: {}, hasLogin: false, userSharedUrl: '' }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { }, onShow: function () { let that = this; //获取用户的登录信息 let userInfo = wx.getStorageSync('userInfo'); this.setData({ userInfo: userInfo, hasLogin: true }); }, getPhoneNumber: function (e) { let that = this; if (e.detail.errMsg !== "getPhoneNumber:ok") { // 拒绝授权 return; } if (!this.data.hasLogin) { wx.showToast({ title: '绑定失败:请先登录', icon: 'none', duration: 2000 }); return; } util.request(api.AuthBindPhone, { iv: e.detail.iv, encryptedData: e.detail.encryptedData }, 'POST').then(function (res) { if (res.errno === 0) { let userInfo = wx.getStorageSync('userInfo'); userInfo.phone = res.data.phone;//设置手机号码 wx.setStorageSync('userInfo', userInfo); that.setData({ userInfo: userInfo, hasLogin: true }); wx.showToast({ title: '绑定手机号码成功', icon: 'success', duration: 2000 }); } }); }, exitLogin: function () { wx.showModal({ title: '', confirmColor: '#b4282d', content: '退出登录?', success: function (res) { if (!res.confirm) { return; } util.request(api.AuthLogout, {}, 'POST'); app.globalData.hasLogin = false; wx.removeStorageSync('token'); wx.removeStorageSync('userInfo'); wx.reLaunch({ url: '/pages/index/index' }); } }) } })
mysql的utf8编码的一个字符最多3个字节,但是一个emoji表情为4个字节,所以utf8不支持存储emoji表情。但是utf8的超集utf8mb4一个字符最多能有4字节,所以能支持emoji表情的存储。
Linux系统中MySQL的配置文件为my.cnf。
Winows中的配置文件为my.ini。
[mysql] # 设置mysql客户端默认字符集 default-character-set=utf8mb4 [mysqld] #设置3306端口 port = 3306 # 设置mysql的安装目录 basedir=D:\\tools\\mysql-5.7.23-winx64 # 设置mysql数据库的数据的存放目录 datadir=D:\\tools\\mysql-5.7.23-winx64\\data # 允许最大连接数 max_connections=200 # 服务端使用的字符集默认为8比特编码的latin1字符集 character-set-server=utf8mb4 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB