SpringCloud微服務實戰——搭建企業級開發框架(五十二):第三方登錄-微信小程式授權登錄流程設計和實現

来源:https://www.cnblogs.com/FullStackProgrammer/archive/2023/03/24/17251399.html
-Advertisement-
Play Games

在前面的設計和實現中,我們的微服務開發平臺通過JustAuth來實現第三方授權登錄,通過集成公共組件,著實減少了很多工作量,大多數的第三方登錄直接通過配置就可以實現。而在第三方授權登錄中,微信小程式授權登錄和APP微信授權登錄是兩種特殊的第三方授權登錄。 JustAuth之所以能夠將多種第三方授權登 ...


  在前面的設計和實現中,我們的微服務開發平臺通過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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 之前用過NXOpen PDM的命名空間下的類,現在記錄一下通過PDM命名空間下的類查詢Teamcenter零組件的信息,也可以用來判斷該零組件是否存在。 1-該工程為DLL工程,直接在NX界面調用,所以直接獲取NXSession。 2-查詢函數advanced用到的查詢為:__NX_STD_ANY_ ...
  • 本文已收錄至Github,推薦閱讀 👉 Java隨想錄 微信公眾號:Java隨想錄 摘要 Redis是一款性能強勁的記憶體資料庫,但是在使用過程中,我們可能會遇到Big Key問題,這個問題就是Redis中某個key的value過大,所以Big Key問題本質是Big Value問題,導致Redis ...
  • 9 月 15 日,Figma 的 CEO Dylan Field 發佈消息:今天,Figma 宣佈接受 Adobe 的收購... Adobe 以約 200 億美元收購 Figma,這也是 Adobe 該公司在其歷史上的最大一筆收購。那是什麼樣的魔力,讓 Figma 被 Adobe 收購呢?下麵以定位 ...
  • 實現Redis的database層(核心層:處理命令並返回) https://github.com/csgopher/go-redis 本文涉及以下文件: dict:定義字典的一些方法 sync_dict:實現dict db:分資料庫 command:定義指令 ping,keys,string:指令 ...
  • Antlr4 簡介 ANTLR(全名:ANother Tool for Language Recognition)是基於LL(*)演算法實現的語法解析器生成器(parser generator),用Java語言編寫,使用自上而下(top-down)的遞歸下降LL剖析器方法。由舊金山大學的Terence ...
  • 導入的文件 前端點擊上傳得到文件(MultipartFile file 【這裡是存放的臨時文件】) 本人前端用的vue3,elementui, 導入按鈕代碼 <!--導入文件 --> <el-col :span="1.5"> <el-button type="info" plain icon="el ...
  • 如題,很簡單,就是先用chardet 庫識別文件編碼,解碼之後再輸出成目標編碼。算是個偶爾能用上的小工具,要用的時候萬一沒有就很難受的那種,比如,網上下載了別人的項目文件,一打開全是亂碼…… 代碼 加了比較詳細的註釋~~ 看懂的要求應該不高,平時用過Python,知道幾個常用庫就行。 from pa ...
  • TIDB簡介 什麼是TIDB TiDB 是一個分散式 NewSQL 資料庫。它支持水平彈性擴展、ACID 事務、標準 SQL、MySQL 語法和 MySQL 協議,具有數據強一致的高可用特性,是一個不僅適合 OLTP 場景還適合 OLAP 場景的混合資料庫。 TiDB 是 PingCAP 公司自主設 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...