天天看點

「SpringCloud」(五十二) 微信小程式授權登入流程設計和實作

作者:AI全棧程式猿

  在前面的設計和實作中,我們的微服務開發平台通過JustAuth來實作第三方授權登入,通過內建公共元件,着實減少了很多工作量,大多數的第三方登入直接通過配置就可以實作。而在第三方授權登入中,微信小程式授權登入和APP微信授權登入是兩種特殊的第三方授權登入。

  JustAuth之是以能夠将多種第三方授權登入服務整合在一起,抽象公共元件的原因是大多數的授權登入伺服器都是遵循OAuth2.0協定開發,雖然略有不同但可通過擴充卡進行轉換為統一接口。微信小程式授權登入和APP的微信授權登入也是OAutn2.0協定的授權登入,但在對接的流程中不是完整的OAuth2.0對接流程。

  通常的第三方授權登入過程中,擷取token的state和code是在回調用戶端url中擷取的,而微信小程式授權登入和APP的微信授權登入擷取token的state和code是使用微信提供的特定方法擷取到的,然後通過微信傳給用戶端,用戶端拿到code之後到背景取擷取openid等微信使用者資訊。然後,再進行系統登入相關操作。

一、微信小程式授權登入、注冊、綁定流程設計說明

  • 微信小程式授權登入、注冊、綁定流程圖:
「SpringCloud」(五十二) 微信小程式授權登入流程設計和實作
  • 微信小程式授權登入、注冊、綁定流程說明:
  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('目前拒絕授權手機号登陸,請使用賬号密碼登入')
      }
    },
           

  通過以上開發基本實作了微信小程式授權登入第三方業務系統的功能,在此基礎上,注冊的功能可以根據業務需求來擴充,大多數網際網路業務,都會是微信小程式授權登入之後就自動注冊使用者。但是有些傳統行業的業務,比如隻有某些公司或組織内部的使用者才能登入,那麼是不允許微信授權登入就自助注冊成系統使用者的。微信小程式前端架構也可以歸根據自己的需求,及擅長的開發方式來選擇,但是微信授權登入的流程是不變的,可以在此基礎上根據業務需求修改優化。