在前面的设计和实现中,我们的微服务开发平台通过JustAuth来实现第三方授权登录,通过集成公共组件,着实减少了很多工作量,大多数的第三方登录直接通过配置就可以实现。而在第三方授权登录中,微信小程序授权登录和APP微信授权登录是两种特殊的第三方授权登录。

  JustAuth之所以能够将多种第三方授权登录服务整合在一起,抽象公共组件的原因是大多数的授权登录服务器都是遵循OAuth2.0协议开发,虽然略有不同但可通过适配器进行转换为统一接口。微信小程序授权登录和APP的微信授权登录也是OAutn2.0协议的授权登录,但在对接的流程中不是完整的OAuth2.0对接流程。

  通常的第三方授权登录过程中,获取token的state和code是在回调客户端url中获取的,而微信小程序授权登录和APP的微信授权登录获取token的state和code是使用微信提供的特定方法获取到的,然后通过微信传给客户端,客户端拿到code之后到后台取获取openid等微信用户信息。然后,再进行系统登录相关操作。

一、微信小程序授权登录、注册、绑定流程设计说明

  • 微信小程序授权登录、注册、绑定流程图:

  • 微信小程序授权登录、注册、绑定流程说明:

    1、用户进入小程序。

    2、小程序前端通过从缓存中获取是否有token来判定用户是否登录。

    3、如果未登录,那么跳转到小程序登录页。

    4、小程序前端执行微信登录方法wx.login获取微信登录的code(此时并未进行微信授权登录)。

    5、小程序前端通过code向业务后台发送请求获取用户唯一的openid。

    6、业务系统根据openid或者unionid判断该用户是否绑定了业务用户,并将是否绑定信息返回给前台。

    7、如果没有绑定过,那么前端展示微信授权登录按钮。

    8、用户点击“授权登录”按钮之后,小程序前端会获取到加密的用户信息。

    9、小程序前端将加密的用户信息传到业务后台进行解密。

    10、业务后台收到加密用户信息后,通过请求微信服务器解密用户信息,并将用户信息存储到业务系统表。

    11、后台将解密后的用户信息(非私密信息)返回到小程序前台。

    12、如果是没有绑定的,那么小程序前台弹出是否获取当前用户手机号的弹出框。

    13、用户选择是否获取微信绑定的手机号来注册或绑定到业务系统的用户。

    14、当用户点击统一获取手机号时,微信会返回加密后的手机号,然后前端将加密后的手机号发送到业务后台解密。

    15、业务后台获取到手机号码之后,会根据手机号码在系统用户表中进行匹配,如果匹配到用户,那么直接返回小程序用户信息。

    16、当用户不同意获取手机号时,那么小程序跳转到输入账号密码进行绑定页面。

    17、当绑定操作执行成功之后,微信小程序调用第三方登录获取token方式,向业务后台获取token。

    18、用户小程序授权登录、注册、绑定成功。

二、微信小程序授权登录、注册、绑定业务后台功能实现

  微信通过其开放平台提供小程序登录功能接口,我们的业务服务可以通过小程序的登录接口方便地获取微信提供的用户身份标识,进而将业务自身用户体系和微信用户相结合,从而更完美地在微信小程序中实现业务功能。

  微信小程序提供了对接登录的SDK,我们只需要按照其官方文档对接开发即可。同时也有很多开源组件将SDK再次进行封装,在业务开发中可以更快速的集成小程序各个接口的调用。

  出于快速开发的原则,同时也少走弯路、少踩坑,我们可以选择一款实现比较完善的组件进行微信小程序的对接。weixin-java-miniapp是集成微信小程序相关SDK操作的工具包,我们在项目中集成此工具包来实现微信小程序授权登录。

1、引入weixin-java-miniapp相关maven依赖,目前发布版本为4.4.0正式版。

  一般在选择开源工具包时,我们不会选择最新版,而是选择稳定版本,但是微信的开放接口经常变动,这里为了能够兼容最新的微信小程序接口,我们在引用包的时候一定要选择更新版本,否则会影响部分接口的调用。

......
<properties>
......
<!-- 微信小程序版本号 -->
<weixin-java-miniapp.version>4.4.0</weixin-java-miniapp.version>
</properties>
......
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>${weixin-java-miniapp.version}</version>
</dependency>
......
2、在配置文件application-dev.yml、application-test.yml、application-prod.yml中新增微信小程序需要的配置项。

  关于小程序如何注册,appid和appsecret如何获取,这里不展开讲,微信开放平台有详细的说明文档。

wx:
miniapp:
configs:
- appid: #微信小程序appid
secret: #微信小程序secret
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON
3、将weixin-java-miniapp配置类文件WxMaConfiguration.java和WxMaProperties.java添加到我们的工程中。
  • WxMaConfiguration.java关键代码
......
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties { private List<Config> configs; @Data
public static class Config {
/**
* 设置微信小程序的appid
*/
private String appid; /**
* 设置微信小程序的Secret
*/
private String secret; /**
* 设置微信小程序消息服务器配置的token
*/
private String token; /**
* 设置微信小程序消息服务器配置的EncodingAESKey
*/
private String aesKey; /**
* 消息格式,XML或者JSON
*/
private String msgDataFormat;
} }
......
  • WxMaProperties.java关键代码
......
private final WxMaProperties properties; @Autowired
public WxMaConfiguration(WxMaProperties properties) {
this.properties = properties;
} @Bean
public WxMaService wxMaService() {
List<WxMaProperties.Config> configs = this.properties.getConfigs();
if (configs == null) {
throw new WxRuntimeException("配置错误!");
}
WxMaService maService = new WxMaServiceImpl();
maService.setMultiConfigs(
configs.stream()
.map(a -> {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(a.getAppid());
config.setSecret(a.getSecret());
config.setToken(a.getToken());
config.setAesKey(a.getAesKey());
config.setMsgDataFormat(a.getMsgDataFormat());
return config;
}).collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, a -> a, (o, n) -> o)));
return maService;
}
......
4、新建WxMaUserController.java用于实现微信小程序请求的相关接口。
  • 实现小程序登录的login接口,此接口会根据微信小程序前端传来的code进行获取用户session_key、openid/unionid,我们的业务系统会根据openid/unionid结合第三方登录表进行用户匹配,如果存在该用户则返回给小程序前台已存在的用户信息;如果不存在,说明该用户是第一次微信小程序登录,那么我们将获取到的微信唯一身份标识加密,并返回微信小程序前台进行下一步绑定账户或注册操作。
    /**
* 登陆接口
*/
@ApiOperation(value = "小程序登录接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "code", value = "小程序code", dataType="String", paramType = "query"),
})
@GetMapping("/login")
public Result<?> login(@PathVariable String appid, String code) { if (StringUtils.isBlank(code)) {
return Result.error("code 不能为空");
} if (!wxMaService.switchover(appid)) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
} WeChatMiniAppLoginDTO weChatMiniAppLoginDTO = new WeChatMiniAppLoginDTO();
try {
WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
weChatMiniAppLoginDTO.setOpenid(session.getOpenid());
weChatMiniAppLoginDTO.setUnionid(session.getUnionid());
// 通过openId获取在系统中是否是已经绑定过的用户,如果没有绑定,那么返回到前台,提示需要绑定或者注册用户
LambdaQueryWrapper<JustAuthSocial> socialLambdaQueryWrapper = new LambdaQueryWrapper<>();
// 如果微信开通了开放平台,那么各个渠道(小程序、公众号等)都会有统一的unionid,如果没开通,就仅仅使用openId
if (StringUtils.isBlank(session.getUnionid()))
{
socialLambdaQueryWrapper.eq(JustAuthSocial::getOpenId, session.getOpenid())
.eq(JustAuthSocial::getSource, "WECHAT_MINI_APP");
}
else
{
socialLambdaQueryWrapper.eq(JustAuthSocial::getUnionId, session.getUnionid())
.and(e -> e.eq(JustAuthSocial::getSource, "WECHAT_MINI_APP")
.or().eq(JustAuthSocial::getSource, "WECHAT_OPEN")
.or().eq(JustAuthSocial::getSource, "WECHAT_MP")
.or().eq(JustAuthSocial::getSource, "WECHAT_ENTERPRISE")
.or().eq(JustAuthSocial::getSource, "WECHAT_APP"));
}
JustAuthSocial justAuthSocial = justAuthSocialService.getOne(socialLambdaQueryWrapper, false);
if (null == justAuthSocial)
{
weChatMiniAppLoginDTO.setUserInfoAlready(false);
weChatMiniAppLoginDTO.setUserBindAlready(false);
justAuthSocial = new JustAuthSocial();
justAuthSocial.setAccessCode(session.getSessionKey());
justAuthSocial.setOpenId(session.getOpenid());
justAuthSocial.setUnionId(session.getUnionid());
justAuthSocial.setSource("WECHAT_MINI_APP");
justAuthSocialService.save(justAuthSocial);
} else {
justAuthSocial.setAccessCode(session.getSessionKey());
justAuthSocialService.updateById(justAuthSocial);
} // 将socialId进行加密返回,用于前端进行第三方登录,获取token
DES des = new DES(Mode.CTS, Padding.PKCS5Padding, secretKey.getBytes(), secretKeySalt.getBytes());
// 这里将source+uuid通过des加密作为key返回到前台
String socialKey = "WECHAT_MINI_APP" + StrPool.UNDERLINE + (StringUtils.isBlank(session.getUnionid()) ? session.getOpenid() : session.getUnionid());
// 将socialKey放入缓存,默认有效期2个小时,如果2个小时未完成验证,那么操作失效,重新获取,在system:socialLoginExpiration配置
redisTemplate.opsForValue().set(AuthConstant.SOCIAL_VALIDATION_PREFIX + socialKey, String.valueOf(justAuthSocial.getId()), socialLoginExpiration,
TimeUnit.SECONDS);
String desSocialKey = des.encryptHex(socialKey);
weChatMiniAppLoginDTO.setBindKey(desSocialKey); // 查询是否绑定用户
// 判断此第三方用户是否被绑定到系统用户
Result<Object> bindResult = justAuthService.userBindQuery(justAuthSocial.getId());
if(null != bindResult && null != bindResult.getData() && bindResult.isSuccess())
{
weChatMiniAppLoginDTO.setUserInfoAlready(true);
weChatMiniAppLoginDTO.setUserBindAlready(true);
} else {
// 这里需要处理返回消息,前端需要根据返回是否已经绑定好的消息来判断
weChatMiniAppLoginDTO.setUserInfoAlready(false);
weChatMiniAppLoginDTO.setUserBindAlready(false);
}
return Result.data(weChatMiniAppLoginDTO);
} catch (WxErrorException e) {
log.error(e.getMessage(), e);
return Result.error("小程序登录失败:" + e);
} finally {
WxMaConfigHolder.remove();//清理ThreadLocal
}
}
  • 实现获取/解密微信小程序用户信息的info接口

      当微信小程序前端获取到用户授权可以获取用户信息时,微信小程序前端将加密的用户信息发送到业务后台,业务后台请求微信服务器将用户信息解密并保存到我们的第三方用户登录表内。
    /**
* 获取用户信息接口
*/
@ApiOperation(value = "小程序获取用户信息接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "socialKey", value = "加密的登录key,用于绑定用户", required = true, dataType="String", paramType = "query"),
@ApiImplicitParam(name = "signature", value = "使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息", required = true, dataType="String", paramType = "query"),
@ApiImplicitParam(name = "rawData", value = "不包括敏感信息的原始数据字符串,用于计算签名", required = true, dataType="String", paramType = "query"),
@ApiImplicitParam(name = "encryptedData", value = "包括敏感数据在内的完整用户信息的加密数据", required = true, dataType="String", paramType = "query"),
@ApiImplicitParam(name = "iv", value = "加密算法的初始向量", required = true, dataType="String", paramType = "query")
})
@GetMapping("/info")
public Result<?> info(@PathVariable String appid, String socialKey,
String signature, String rawData, String encryptedData, String iv) {
if (!wxMaService.switchover(appid)) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
} // 查询第三方用户信息
JustAuthSocial justAuthSocial = this.getJustAuthSocial(socialKey);
if (StringUtils.isBlank(justAuthSocial.getAccessCode()))
{
throw new BusinessException("登录状态失效,请尝试重新进入小程序");
}
// 用户信息校验
if (!wxMaService.getUserService().checkUserInfo(justAuthSocial.getAccessCode(), rawData, signature)) {
WxMaConfigHolder.remove();//清理ThreadLocal
return Result.error("user check failed");
} // 解密用户信息
WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(justAuthSocial.getAccessCode(), encryptedData, iv);
WxMaConfigHolder.remove();//清理ThreadLocal
justAuthSocial.setAvatar(userInfo.getAvatarUrl());
justAuthSocial.setUnionId(userInfo.getUnionId());
justAuthSocial.setNickname(userInfo.getNickName());
justAuthSocialService.updateById(justAuthSocial);
return Result.data(userInfo);
}
  • 当在小程序前端绑定或注册账号时,可以在用户允许的前提下获取微信用户的手机号,同样,手机号和用户信息都是需要传到业务后台,然后再请求微信服务器进行解密。获取到手机号之后,业务后台根据手机号和系统用户进行匹配,如果存在,那么直接将微信账号绑定到我们业务系统的当前手机号用户。如果不存在,那么返回微信小程序不存在绑定用户的信息,由小程序前端继续进行绑定或者注册操作。
    /**
* 获取用户绑定手机号信息
*/
@ApiOperation(value = "小程序获取用户绑定手机号信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "socialKey", value = "加密的登录key,用于绑定用户", required = true, dataType="String", paramType = "query"),
@ApiImplicitParam(name = "encryptedData", value = "包括敏感数据在内的完整用户信息的加密数据", required = true, dataType="String", paramType = "query"),
@ApiImplicitParam(name = "iv", value = "加密算法的初始向量", required = true, dataType="String", paramType = "query")
})
@GetMapping("/phone")
public Result<?> phone(@PathVariable String appid, String socialKey, String encryptedData, String iv) {
if (!wxMaService.switchover(appid)) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
}
// 查询第三方用户信息
JustAuthSocial justAuthSocial = this.getJustAuthSocial(socialKey);
if (StringUtils.isBlank(justAuthSocial.getAccessCode()))
{
throw new BusinessException("登录状态失效,请尝试重新进入小程序");
} // 解密
WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(justAuthSocial.getAccessCode(), encryptedData, iv);
WxMaConfigHolder.remove();//清理ThreadLocal // 不带区号的手机,国外的手机会带区号
String phoneNumber = phoneNoInfo.getPurePhoneNumber();
// 查询用户是否存在,如果存在,那么直接调用绑定接口
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getMobile, phoneNumber);
User userInfo = userService.getOne(lambdaQueryWrapper);
Long userId;
// 判断返回信息
if (null != userInfo && null != userInfo.getId()) {
userId = userInfo.getId();
}
else {
// 如果用户不存在,那么调用新建用户接口,并绑定
CreateUserDTO createUserDTO = new CreateUserDTO();
createUserDTO.setAccount(phoneNumber);
createUserDTO.setMobile(phoneNumber);
createUserDTO.setNickname(StringUtils.isBlank(justAuthSocial.getNickname()) ? phoneNumber : justAuthSocial.getNickname());
createUserDTO.setPassword(StringUtils.isBlank(justAuthSocial.getUnionId()) ? justAuthSocial.getOpenId() : justAuthSocial.getUnionId());
createUserDTO.setStatus(GitEggConstant.UserStatus.ENABLE);
createUserDTO.setAvatar(justAuthSocial.getAvatar());
createUserDTO.setEmail(justAuthSocial.getEmail());
createUserDTO.setStreet(justAuthSocial.getLocation());
createUserDTO.setComments(justAuthSocial.getRemark());
CreateUserDTO resultUserAdd = userService.createUser(createUserDTO);
if (null != resultUserAdd && null != resultUserAdd.getId()) {
userId = resultUserAdd.getId();
} else {
// 如果添加失败,则返回失败信息
return Result.data(resultUserAdd);
}
}
// 执行绑定操作
justAuthService.userBind(justAuthSocial.getId(), userId);
return Result.success("账号绑定成功");
}
  • 提供绑定当前登录账号接口,由微信小程序前端进行调用绑定
    /**
* 绑定当前登录账号
*/
@ApiOperation(value = "绑定当前登录账号")
@ApiImplicitParams({
@ApiImplicitParam(name = "socialKey", value = "加密的登录key,用于绑定用户", required = true, dataType="String", paramType = "query")
})
@GetMapping("/bind")
public Result<?> bind(@PathVariable String appid, @NotBlank String socialKey, @CurrentUser GitEggUser user) {
if (!wxMaService.switchover(appid)) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
}
if (null == user || (null != user && null == user.getId())) {
throw new BusinessException("用户未登录");
}
// 查询第三方用户信息
JustAuthSocial justAuthSocial = this.getJustAuthSocial(socialKey);
if (StringUtils.isBlank(justAuthSocial.getAccessCode()))
{
throw new BusinessException("账号绑定失败,请尝试重新进入小程序");
}
// 执行绑定操作
justAuthService.userBind(justAuthSocial.getId(), user.getId());
return Result.success("账号绑定成功");
}
  • 提供解绑接口,除了绑定接口外,我们系统服务应提供微信小程序解绑功能,这里实现解绑接口
    /**
* 解绑当前登录账号
*/
@ApiOperation(value = "解绑当前登录账号")
@GetMapping("/unbind")
public Result<?> unbind(@PathVariable String appid, @CurrentUser GitEggUser user) {
if (!wxMaService.switchover(appid)) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
}
if (null == user || (null != user && null == user.getId())) {
throw new BusinessException("用户未登录");
}
LambdaQueryWrapper<JustAuthSocialUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(JustAuthSocialUser::getUserId, user.getId());
justAuthSocialUserService.remove(queryWrapper);
return Result.success("账号解绑成功");
}

  通过以上接口的功能,基本实现了微信小程序前端进行绑定、注册以及获取用户信息、用户手机号所需要的接口,下面来实现小程序前端具体的业务实现。

三、微信小程序授权登录、注册、绑定小程序前端功能实现。

  微信小程序前端开发有多种方式,可以使用微信小程序官方开发方式,也可以使用第三方的开发方式。因为大多数前端都会使用Vue.js开发,而mpvue可以使用开发Vue.js的方式来开发微信小程序,所以这里我们选择使用mpvue来开发微信小程序。这里不详细讲解mpvue框架的搭建过程,只详细说明微信小程序授权登录相关功能,有需要的可以参考mpvue官方文档。

1、定义微信小程序授权登录相关接口文件login.js,将我们业务后台实现的接口统一管理和调用。

  因为我们的开发框架是支持多租户的,同时也是支持多个小程序的,为了同一套后台可以支持多个微信小程序,这里选择在发布的微信小程序中配置appId,由微信小程序前端参数来确定具体的微信小程序。

import fly from '@/utils/requestWx'

// 获取用户信息
export function getOpenId (params) {
return fly.get(`/wx/user/${params.appId}/login`, params)
} // 获取用户信息
export function getUserInfo (params) {
return fly.get(`/wx/user/${params.appId}/info`, params)
} // 获取用户手机号
export function getUserPhone (params) {
return fly.get(`/wx/user/${params.appId}/phone`, params)
} // 绑定微信账号
export function bindWeChatUser (params) {
return fly.get(`/wx/user/${params.appId}/bind`, params)
} // 解绑微信账号
export function unbindWeChatUser (params) {
return fly.get(`/wx/user/${params.appId}/unbind`)
} // 登录
export function postToken (params) {
return fly.post(`/oauth/token`, params)
} // 退出登录
export function logout () {
return fly.post(`/oauth/logout`)
} // 获取登录用户信息
export function getLoginUserInfo () {
return fly.get(`/system/account/user/info`)
}
2、新增login.vue授权登录页面,实现微信小程序具体授权登录、绑定、注册的操作界面。
  • 增加微信授权登录按钮
    <div class="login-btn" v-show="!showAccountLogin">
<van-button color="#1aad19" block open-type="getUserInfo" @getuserinfo="bindGetUserInfo" >微信授权登录</van-button>
</div>
  • 增加微信小程序获取微信用户绑定手机号的弹出框,应微信官方要求,必须由用户点击授权之后,才能够获取用户绑定的手机号。
    <van-dialog
title="授权验证"
@getphonenumber="bindGetUserPhone"
confirm-button-open-type="getPhoneNumber"
message="微信一键登录需要绑定您的手机号"
:show="showUserPhoneVisible">
</van-dialog>
  • 增加账号绑定弹出框,同样微信官方要求,获取微信用户信息,必须由用户点击授权允许,所以这里弹出框里的按钮也是微信用户同意授权获取用户信息的按钮。
    <van-dialog
title="账号绑定"
@getuserinfo="bindUserInfo"
confirm-button-open-type="getUserInfo"
message="登录成功,是否将账号绑定到当前微信?"
:show="showUserInfoVisible">
</van-dialog>
3、在methods中新增微信绑定相关方法来具体实现微信小程序授权登录、绑定、注册等操作接口的调用。
  • 通过wx.login拿到code,在后台通过openId判断是否已经绑定用户,如果已绑定用户,则不需要再进行用户授权操作,直接登录.
wxLogin () {
var that = this
wx.login({
success (res) {
that.code = res.code
const params = {
appId: appId,
code: res.code
}
getOpenId(params).then(res => {
if (res.code === 200 && res.data) {
const result = res.data
mpvue.setStorageSync('openid', result.openid)
mpvue.setStorageSync('unionid', result.unionid)
mpvue.setStorageSync('bindKey', result.bindKey)
mpvue.setStorageSync('userBindAlready', result.userBindAlready)
// 1、如果绑定过,那么直接使用绑定用户登录
// 2、如果没有绑定过,那弹出获取用户信息和获取手机号信息进行绑定
if (result.userBindAlready) {
const loginParams = {
grant_type: 'social',
social_key: mpvue.getStorageSync('bindKey')
}
postToken(loginParams).then(res => {
if (res.code === 200) {
console.log(res)
const data = res.data
mpvue.setStorageSync('token', data.token)
mpvue.setStorageSync('refreshToken', data.refreshToken)
// 获取用户信息
that.loginSuccess()
} else {
Toast(res.msg)
}
})
}
} else {
Toast(res.msg)
}
})
}
})
},
  • 获取微信用户信息实现登陆,微信小程序接口,只允许点击按钮,用户同意后才能获取用户信息,不要直接使用wx.getUserInfo,此接口已过期,微信不再支持。
    bindGetUserInfo: function (res) {
var that = this
if (res.mp.detail.errMsg === 'getUserInfo:ok') {
const userParams = {
appId: appId,
socialKey: mpvue.getStorageSync('bindKey'),
signature: res.mp.detail.signature,
rawData: res.mp.detail.rawData,
encryptedData: res.mp.detail.encryptedData,
iv: res.mp.detail.iv
}
getUserInfo(userParams).then(response => {
const userBindAlready = mpvue.getStorageSync('userBindAlready')
// 1、如果绑定过,那么直接使用绑定用户登录
// 2、如果没有绑定过,那弹出获取用户信息和获取手机号信息进行绑定
if (userBindAlready) {
const loginParams = {
grant_type: 'social',
social_key: mpvue.getStorageSync('bindKey')
}
postToken(loginParams).then(res => {
if (res.code === 200) {
console.log(res)
const data = res.data
mpvue.setStorageSync('token', data.token)
mpvue.setStorageSync('refreshToken', data.refreshToken)
// 获取用户信息
that.loginSuccess()
} else {
// 弹出获取手机号授权按钮
that.showUserPhoneVisible = true
}
})
} else {
// 弹出获取手机号授权按钮
that.showUserPhoneVisible = true
}
})
} else {
console.log('点击了拒绝')
}
},
  • 微信通过用户点击授权获取手机号来实现账号绑定操作,如果用户点击拒绝,那么提示用户无法绑定当前用户微信绑定的手机号,请用户继续授权。
    bindGetUserPhone (e) {
const that = this
if (e.mp.detail.errMsg === 'getPhoneNumber:ok') {
console.log(e.mp.detail)
// 写入store
const params = {
appId: appId,
socialKey: mpvue.getStorageSync('bindKey'),
encryptedData: e.mp.detail.encryptedData,
iv: e.mp.detail.iv
}
getUserPhone(params).then(res => {
if (res.code === 200) {
console.log(res)
const loginParams = {
grant_type: 'social',
social_key: mpvue.getStorageSync('bindKey')
}
postToken(loginParams).then(res => {
if (res.code === 200) {
console.log(res)
const data = res.data
mpvue.setStorageSync('token', data.token)
mpvue.setStorageSync('refreshToken', data.refreshToken)
// 获取用户信息
that.loginSuccess()
} else { }
})
} else {
that.showUserPhoneVisible = false
// 获取用户信息
that.loginSuccess()
Toast(res.msg)
}
})
} else {
that.showUserPhoneVisible = false
Toast('当前拒绝授权手机号登陆,请使用账号密码登录')
}
},

  通过以上开发基本实现了微信小程序授权登录第三方业务系统的功能,在此基础上,注册的功能可以根据业务需求来扩展,大多数互联网业务,都会是微信小程序授权登录之后就自动注册用户。但是有些传统行业的业务,比如只有某些公司或组织内部的用户才能登录,那么是不允许微信授权登录就自助注册成系统用户的。微信小程序前端框架也可以归根据自己的需求,及擅长的开发方式来选择,但是微信授权登录的流程是不变的,可以在此基础上根据业务需求修改优化。

源码地址:

Gitee: https://gitee.com/wmz1930/GitEgg

GitHub: https://github.com/wmz1930/GitEgg

SpringCloud微服务实战——搭建企业级开发框架(五十二):第三方登录-微信小程序授权登录流程设计和实现的更多相关文章

  1. SpringCloud微服务实战——搭建企业级开发框架(十二):OpenFeign+Ribbon实现负载均衡

      Ribbon是Netflix下的负载均衡项目,它主要实现中间层应用程序的负载均衡.为Ribbon配置服务提供者地址列表后,Ribbon就会基于某种负载均衡算法,自动帮助服务调用者去请求.Ribbo ...

  2. SpringCloud微服务实战——搭建企业级开发框架(十五):集成Sentinel高可用流量管理框架【熔断降级】

      Sentinel除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一.由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积.Sentinel ...

  3. SpringCloud微服务实战——搭建企业级开发框架(十):使用Nacos分布式配置中心

    随着业务的发展.微服务架构的升级,服务的数量.程序的配置日益增多(各种微服务.各种服务器地址.各种参数),传统的配置文件方式和数据库的方式已无法满足开发人员对配置管理的要求: 安全性:配置跟随源代码保 ...

  4. SpringCloud微服务实战——搭建企业级开发框架(十四):集成Sentinel高可用流量管理框架【限流】

      Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流.流量整形.熔断降级.系统负载保护.热点防护等多个维度来帮助开发者保障微服务的稳定性. Sentinel 具有 ...

  5. SpringCloud微服务实战——搭建企业级开发框架(十六):集成Sentinel高可用流量管理框架【自定义返回消息】

    Sentinel限流之后,默认的响应消息为Blocked by Sentinel (flow limiting),对于系统整体功能提示来说并不统一,参考我们前面设置的统一响应及异常处理方式,返回相同的 ...

  6. SpringCloud微服务实战——搭建企业级开发框架(十九):Gateway使用knife4j聚合微服务文档

      本章介绍Spring Cloud Gateway网关如何集成knife4j,通过网关聚合所有的Swagger微服务文档 1.gitegg-gateway中引入knife4j依赖,如果没有后端代码编 ...

  7. SpringCloud微服务实战——搭建企业级开发框架(四十六):【移动开发】整合uni-app搭建移动端快速开发框架-环境搭建

      近年来uni-app发展势头迅猛,只要会vue.js,就可以开发一套代码,发布移动应用到iOS.Android.Web(响应式).以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/ ...

  8. SpringCloud微服务实战——搭建企业级开发框架(三十五):SpringCloud + Docker + k8s实现微服务集群打包部署-集群环境部署

    一.集群环境规划配置 生产环境不要使用一主多从,要使用多主多从.这里使用三台主机进行测试一台Master(172.16.20.111),两台Node(172.16.20.112和172.16.20.1 ...

  9. SpringCloud微服务实战——搭建企业级开发框架(二十三):Gateway+OAuth2+JWT实现微服务统一认证授权

      OAuth2是一个关于授权的开放标准,核心思路是通过各类认证手段(具体什么手段OAuth2不关心)认证用户身份,并颁发token(令牌),使得第三方应用可以使用该token(令牌)在限定时间.限定 ...

  10. SpringCloud微服务实战——搭建企业级开发框架(四十):使用Spring Security OAuth2实现单点登录(SSO)系统

    一.单点登录SSO介绍   目前每家企业或者平台都存在不止一套系统,由于历史原因每套系统采购于不同厂商,所以系统间都是相互独立的,都有自己的用户鉴权认证体系,当用户进行登录系统时,不得不记住每套系统的 ...

随机推荐

  1. web测试:test过程中接口报错 "Object reference not set to an instance of an object."

    "Object reference not set to an instance of an object." 对象引用未设置为对象的实例 可能原因: 1.参数类型传错,或少传参数 ...

  2. UI自动化之【报错记录-selenium】

    1.找不到元素 写脚本的过程中时不时就会报这种错,一般路径定位不到直接复制xpath基本就能找到了,也有时候是因为有iframe或是句柄不对 原因: ①没有加等待时间,脚本运行到那步时,页面还没加载完 ...

  3. idea中ueditor的入门

    首先在https://github.com/fex-team/ueditor下载ueditor1_4_3_3-utf8-jsp.zip:解压去掉里边jsp中的bin目录放到项目中的webapp中: 添 ...

  4. javascript数据类型,定义方法,(工厂模式及闭包的应用)

    js数据类型分为两大类:一  值类型                                        二 引用类型 一 值类型  string  number  boolean null ...

  5. 12.6linux学习第十三天

    今天老刘开始讲第9第章使用ssh服务管理远程主机.第10章开了个小头. 9.1 配置网卡服务 9.1.1 配置网卡参数 截至目前,大家已经完全可以利用当前所学的知识来管理Linux系统了.当然,大家的 ...

  6. sql学习笔记 - 1 数据库简介

    数据库简介 """ 本学习笔记来自哔哩哔哩--老男孩Python全栈开发29期全套 https://www.bilibili.com/video/BV1QE41147hU ...

  7. Qt打印不同颜色

    qCritical()<<"\033[47;31m"<<"打印的字符串"<<"\033[m"; \033 ...

  8. SpringBoot - Lombok使用详解3(@NoArgsConstructor、@AllArgsConstructor、@RequiredArgsConstructor)

    五.Lombok 注解详解(3) 5,@NoArgsConstructor 注解在类上,为类提供一个无参的构造方法. 注意: 当类中有 final 字段没有被初始化时,编译器会报错,此时可用 @NoA ...

  9. POE供电及PD的功率分级

    POE供电标准有IEEE802.3af和IEEE802.3at 802.3af,PSE最高提供15.5W的功率而PD端则为12.95W: 802.3at,PSE最高提供30W的功率而PD端则为25W: ...

  10. 估计人数【最小路径重复点覆盖】【直接在(i,j)建一个新点】

    估计人数 题意 思路 用最少的人,走完这几条线.最小重复路径点覆盖问题 建图之后,跑一下二分图. 考虑建图:图中'1'连着完下.或者右走.我们把图中所有的1编号,然后建图,然后floly,然后匈牙利. ...