亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

 在前面的設(shè)計(jì)和實(shí)現(xiàn)中,我們的微服務(wù)開發(fā)平臺(tái)通過(guò)JustAuth來(lái)實(shí)現(xiàn)第三方授權(quán)登錄,通過(guò)集成公共組件,著實(shí)減少了很多工作量,大多數(shù)的第三方登錄直接通過(guò)配置就可以實(shí)現(xiàn)。而在第三方授權(quán)登錄中,微信小程序授權(quán)登錄和App微信授權(quán)登錄是兩種特殊的第三方授權(quán)登錄。
  JustAuth之所以能夠?qū)⒍喾N第三方授權(quán)登錄服務(wù)整合在一起,抽象公共組件的原因是大多數(shù)的授權(quán)登錄服務(wù)器都是遵循OAuth2.0協(xié)議開發(fā),雖然略有不同但可通過(guò)適配器進(jìn)行轉(zhuǎn)換為統(tǒng)一接口。微信小程序授權(quán)登錄和APP的微信授權(quán)登錄也是OAutn2.0協(xié)議的授權(quán)登錄,但在對(duì)接的流程中不是完整的OAuth2.0對(duì)接流程。
  通常的第三方授權(quán)登錄過(guò)程中,獲取token的state和code是在回調(diào)客戶端url中獲取的,而微信小程序授權(quán)登錄和APP的微信授權(quán)登錄獲取token的state和code是使用微信提供的特定方法獲取到的,然后通過(guò)微信傳給客戶端,客戶端拿到code之后到后臺(tái)取獲取openid等微信用戶信息。然后,再進(jìn)行系統(tǒng)登錄相關(guān)操作。

一、微信小程序授權(quán)登錄、注冊(cè)、綁定流程設(shè)計(jì)說(shuō)明

  • 微信小程序授權(quán)登錄、注冊(cè)、綁定流程圖:

 

  • 微信小程序授權(quán)登錄、注冊(cè)、綁定流程說(shuō)明:
  1. 用戶進(jìn)入小程序。
  2. 小程序前端通過(guò)從緩存中獲取是否有token來(lái)判定用戶是否登錄。
  3. 如果未登錄,那么跳轉(zhuǎn)到小程序登錄頁(yè)。
  4. 小程序前端執(zhí)行微信登錄方法wx.login獲取微信登錄的code(此時(shí)并未進(jìn)行微信授權(quán)登錄)。
  5. 小程序前端通過(guò)code向業(yè)務(wù)后臺(tái)發(fā)送請(qǐng)求獲取用戶唯一的openid。
  6. 業(yè)務(wù)系統(tǒng)根據(jù)openid或者unionid判斷該用戶是否綁定了業(yè)務(wù)用戶,并將是否綁定信息返回給前臺(tái)。
  7. 如果沒(méi)有綁定過(guò),那么前端展示微信授權(quán)登錄按鈕。
  8. 用戶點(diǎn)擊“授權(quán)登錄”按鈕之后,小程序前端會(huì)獲取到加密的用戶信息。
  9. 小程序前端將加密的用戶信息傳到業(yè)務(wù)后臺(tái)進(jìn)行解密。
  10. 業(yè)務(wù)后臺(tái)收到加密用戶信息后,通過(guò)請(qǐng)求微信服務(wù)器解密用戶信息,并將用戶信息存儲(chǔ)到業(yè)務(wù)系統(tǒng)表。
  11. 后臺(tái)將解密后的用戶信息(非私密信息)返回到小程序前臺(tái)。
  12. 如果是沒(méi)有綁定的,那么小程序前臺(tái)彈出是否獲取當(dāng)前用戶手機(jī)號(hào)的彈出框。
  13. 用戶選擇是否獲取微信綁定的手機(jī)號(hào)來(lái)注冊(cè)或綁定到業(yè)務(wù)系統(tǒng)的用戶。
  14. 當(dāng)用戶點(diǎn)擊統(tǒng)一獲取手機(jī)號(hào)時(shí),微信會(huì)返回加密后的手機(jī)號(hào),然后前端將加密后的手機(jī)號(hào)發(fā)送到業(yè)務(wù)后臺(tái)解密。
  15. 業(yè)務(wù)后臺(tái)獲取到手機(jī)號(hào)碼之后,會(huì)根據(jù)手機(jī)號(hào)碼在系統(tǒng)用戶表中進(jìn)行匹配,如果匹配到用戶,那么直接返回小程序用戶信息。
  16. 當(dāng)用戶不同意獲取手機(jī)號(hào)時(shí),那么小程序跳轉(zhuǎn)到輸入賬號(hào)密碼進(jìn)行綁定頁(yè)面。
  17. 當(dāng)綁定操作執(zhí)行成功之后,微信小程序調(diào)用第三方登錄獲取token方式,向業(yè)務(wù)后臺(tái)獲取token。
  18. 用戶小程序授權(quán)登錄、注冊(cè)、綁定成功。

二、微信小程序授權(quán)登錄、注冊(cè)、綁定業(yè)務(wù)后臺(tái)功能實(shí)現(xiàn)

  微信通過(guò)其開放平臺(tái)提供小程序登錄功能接口,我們的業(yè)務(wù)服務(wù)可以通過(guò)小程序的登錄接口方便地獲取微信提供的用戶身份標(biāo)識(shí),進(jìn)而將業(yè)務(wù)自身用戶體系和微信用戶相結(jié)合,從而更完美地在微信小程序中實(shí)現(xiàn)業(yè)務(wù)功能。
  微信小程序提供了對(duì)接登錄的SDK,我們只需要按照其官方文檔對(duì)接開發(fā)即可。同時(shí)也有很多開源組件將SDK再次進(jìn)行封裝,在業(yè)務(wù)開發(fā)中可以更快速的集成小程序各個(gè)接口的調(diào)用。
  出于快速開發(fā)的原則,同時(shí)也少走彎路、少踩坑,我們可以選擇一款實(shí)現(xiàn)比較完善的組件進(jìn)行微信小程序的對(duì)接。weixin-JAVA-miniapp是集成微信小程序相關(guān)SDK操作的工具包,我們?cè)陧?xiàng)目中集成此工具包來(lái)實(shí)現(xiàn)微信小程序授權(quán)登錄。

1、引入weixin-java-miniapp相關(guān)maven依賴,目前發(fā)布版本為4.4.0正式版。

  一般在選擇開源工具包時(shí),我們不會(huì)選擇最新版,而是選擇穩(wěn)定版本,但是微信的開放接口經(jīng)常變動(dòng),這里為了能夠兼容最新的微信小程序接口,我們?cè)谝冒臅r(shí)候一定要選擇更新版本,否則會(huì)影響部分接口的調(diào)用。

......
    <properties>
......
        <!-- 微信小程序版本號(hào) -->
        <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中新增微信小程序需要的配置項(xiàng)。

  關(guān)于小程序如何注冊(cè),appid和appsecret如何獲取,這里不展開講,微信開放平臺(tái)有詳細(xì)的說(shuō)明文檔。

wx:
  miniapp:
    configs:
      - appid: #微信小程序appid
        secret: #微信小程序secret
        token: #微信小程序消息服務(wù)器配置的token
        aesKey: #微信小程序消息服務(wù)器配置的EncodingAESKey
        msgDataFormat: JSON

3、將weixin-java-miniapp配置類文件Wxmaconfiguration.java和WxMaProperties.java添加到我們的工程中。

  • WxMaConfiguration.java關(guān)鍵代碼
......
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {

    private List<Config> configs;

    @Data
    public static class Config {
        /**
         * 設(shè)置微信小程序的appid
         */
        private String appid;

        /**
         * 設(shè)置微信小程序的Secret
         */
        private String secret;

        /**
         * 設(shè)置微信小程序消息服務(wù)器配置的token
         */
        private String token;

        /**
         * 設(shè)置微信小程序消息服務(wù)器配置的EncodingAESKey
         */
        private String aesKey;

        /**
         * 消息格式,XML或者JSON
         */
        private String msgDataFormat;
    }

}
......
  • WxMaProperties.java關(guān)鍵代碼
......
    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("配置錯(cuò)誤!");
        }
        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用于實(shí)現(xiàn)微信小程序請(qǐng)求的相關(guān)接口。

  • 實(shí)現(xiàn)小程序登錄的login接口,此接口會(huì)根據(jù)微信小程序前端傳來(lái)的code進(jìn)行獲取用戶session_key、openid/unionid,我們的業(yè)務(wù)系統(tǒng)會(huì)根據(jù)openid/unionid結(jié)合第三方登錄表進(jìn)行用戶匹配,如果存在該用戶則返回給小程序前臺(tái)已存在的用戶信息;如果不存在,說(shuō)明該用戶是第一次微信小程序登錄,那么我們將獲取到的微信唯一身份標(biāo)識(shí)加密,并返回微信小程序前臺(tái)進(jìn)行下一步綁定賬戶或注冊(cè)操作。
    /**
     * 登陸接口
     */
    @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("未找到對(duì)應(yīng)appid=[%s]的配置,請(qǐng)核實(shí)!", appid));
        }
    
        WeChatMiniAppLoginDTO weChatMiniAppLoginDTO = new WeChatMiniAppLoginDTO();
        try {
            WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
            weChatMiniAppLoginDTO.setOpenid(session.getOpenid());
            weChatMiniAppLoginDTO.setUnionid(session.getUnionid());
            // 通過(guò)openId獲取在系統(tǒng)中是否是已經(jīng)綁定過(guò)的用戶,如果沒(méi)有綁定,那么返回到前臺(tái),提示需要綁定或者注冊(cè)用戶
            LambdaQueryWrapper<JustAuthSocial> socialLambdaQueryWrapper = new LambdaQueryWrapper<>();
            // 如果微信開通了開放平臺(tái),那么各個(gè)渠道(小程序、公眾號(hào)等)都會(huì)有統(tǒng)一的unionid,如果沒(méi)開通,就僅僅使用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進(jìn)行加密返回,用于前端進(jìn)行第三方登錄,獲取token
            DES des = new DES(Mode.CTS, Padding.PKCS5Padding, secretKey.getBytes(), secretKeySalt.getBytes());
            // 這里將source+uuid通過(guò)des加密作為key返回到前臺(tái)
            String socialKey = "WECHAT_MINI_APP" + StrPool.UNDERLINE + (StringUtils.isBlank(session.getUnionid()) ? session.getOpenid() : session.getUnionid());
            // 將socialKey放入緩存,默認(rèn)有效期2個(gè)小時(shí),如果2個(gè)小時(shí)未完成驗(yàn)證,那么操作失效,重新獲取,在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);
            
            // 查詢是否綁定用戶
            // 判斷此第三方用戶是否被綁定到系統(tǒng)用戶
            Result<Object> bindResult = justAuthService.userBindQuery(justAuthSocial.getId());
            if(null != bindResult && null != bindResult.getData() && bindResult.isSuccess())
            {
                weChatMiniAppLoginDTO.setUserInfoAlready(true);
                weChatMiniAppLoginDTO.setUserBindAlready(true);
            } else {
                // 這里需要處理返回消息,前端需要根據(jù)返回是否已經(jīng)綁定好的消息來(lái)判斷
                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
        }
    }
  • 實(shí)現(xiàn)獲取/解密微信小程序用戶信息的info接口
      當(dāng)微信小程序前端獲取到用戶授權(quán)可以獲取用戶信息時(shí),微信小程序前端將加密的用戶信息發(fā)送到業(yè)務(wù)后臺(tái),業(yè)務(wù)后臺(tái)請(qǐng)求微信服務(wù)器將用戶信息解密并保存到我們的第三方用戶登錄表內(nèi)。
    /**
     * 獲取用戶信息接口
     */
    @ApiOperation(value = "小程序獲取用戶信息接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "socialKey", value = "加密的登錄key,用于綁定用戶", required = true, dataType="String", paramType = "query"),
            @ApiImplicitParam(name = "signature", value = "使用 sha1( rawData + sessionkey ) 得到字符串,用于校驗(yàn)用戶信息", required = true, dataType="String", paramType = "query"),
            @ApiImplicitParam(name = "rawData", value = "不包括敏感信息的原始數(shù)據(jù)字符串,用于計(jì)算簽名", required = true, dataType="String", paramType = "query"),
            @ApiImplicitParam(name = "encryptedData", value = "包括敏感數(shù)據(jù)在內(nèi)的完整用戶信息的加密數(shù)據(jù)", 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("未找到對(duì)應(yīng)appid=[%s]的配置,請(qǐng)核實(shí)!", appid));
        }
    
        // 查詢第三方用戶信息
        JustAuthSocial justAuthSocial = this.getJustAuthSocial(socialKey);
        if (StringUtils.isBlank(justAuthSocial.getAccessCode()))
        {
            throw new BusinessException("登錄狀態(tài)失效,請(qǐng)嘗試重新進(jìn)入小程序");
        }
        // 用戶信息校驗(yàn)
        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);
    }
  • 當(dāng)在小程序前端綁定或注冊(cè)賬號(hào)時(shí),可以在用戶允許的前提下獲取微信用戶的手機(jī)號(hào),同樣,手機(jī)號(hào)和用戶信息都是需要傳到業(yè)務(wù)后臺(tái),然后再請(qǐng)求微信服務(wù)器進(jìn)行解密。獲取到手機(jī)號(hào)之后,業(yè)務(wù)后臺(tái)根據(jù)手機(jī)號(hào)和系統(tǒng)用戶進(jìn)行匹配,如果存在,那么直接將微信賬號(hào)綁定到我們業(yè)務(wù)系統(tǒng)的當(dāng)前手機(jī)號(hào)用戶。如果不存在,那么返回微信小程序不存在綁定用戶的信息,由小程序前端繼續(xù)進(jìn)行綁定或者注冊(cè)操作。
    /**
     * 獲取用戶綁定手機(jī)號(hào)信息
     */
    @ApiOperation(value = "小程序獲取用戶綁定手機(jī)號(hào)信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "socialKey", value = "加密的登錄key,用于綁定用戶", required = true, dataType="String", paramType = "query"),
            @ApiImplicitParam(name = "encryptedData", value = "包括敏感數(shù)據(jù)在內(nèi)的完整用戶信息的加密數(shù)據(jù)", 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("未找到對(duì)應(yīng)appid=[%s]的配置,請(qǐng)核實(shí)!", appid));
        }
        // 查詢第三方用戶信息
        JustAuthSocial justAuthSocial = this.getJustAuthSocial(socialKey);
        if (StringUtils.isBlank(justAuthSocial.getAccessCode()))
        {
            throw new BusinessException("登錄狀態(tài)失效,請(qǐng)嘗試重新進(jìn)入小程序");
        }

        // 解密
        WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(justAuthSocial.getAccessCode(), encryptedData, iv);
        WxMaConfigHolder.remove();//清理ThreadLocal
        
        // 不帶區(qū)號(hào)的手機(jī),國(guó)外的手機(jī)會(huì)帶區(qū)號(hào)
        String phoneNumber = phoneNoInfo.getPurePhoneNumber();
        // 查詢用戶是否存在,如果存在,那么直接調(diào)用綁定接口
        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 {
            // 如果用戶不存在,那么調(diào)用新建用戶接口,并綁定
            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);
            }
        }
        // 執(zhí)行綁定操作
        justAuthService.userBind(justAuthSocial.getId(), userId);
        return Result.success("賬號(hào)綁定成功");
    }
  • 提供綁定當(dāng)前登錄賬號(hào)接口,由微信小程序前端進(jìn)行調(diào)用綁定
    /**
     * 綁定當(dāng)前登錄賬號(hào)
     */
    @ApiOperation(value = "綁定當(dāng)前登錄賬號(hào)")
    @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("未找到對(duì)應(yīng)appid=[%s]的配置,請(qǐng)核實(shí)!", 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("賬號(hào)綁定失敗,請(qǐng)嘗試重新進(jìn)入小程序");
        }
        // 執(zhí)行綁定操作
        justAuthService.userBind(justAuthSocial.getId(), user.getId());
        return Result.success("賬號(hào)綁定成功");
    }
  • 提供解綁接口,除了綁定接口外,我們系統(tǒng)服務(wù)應(yīng)提供微信小程序解綁功能,這里實(shí)現(xiàn)解綁接口
    /**
     * 解綁當(dāng)前登錄賬號(hào)
     */
    @ApiOperation(value = "解綁當(dāng)前登錄賬號(hào)")
    @GetMapping("/unbind")
    public Result<?> unbind(@PathVariable String appid, @CurrentUser GitEggUser user) {
        if (!wxMaService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到對(duì)應(yīng)appid=[%s]的配置,請(qǐng)核實(shí)!", 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("賬號(hào)解綁成功");
    }

  通過(guò)以上接口的功能,基本實(shí)現(xiàn)了微信小程序前端進(jìn)行綁定、注冊(cè)以及獲取用戶信息、用戶手機(jī)號(hào)所需要的接口,下面來(lái)實(shí)現(xiàn)小程序前端具體的業(yè)務(wù)實(shí)現(xiàn)。

三、微信小程序授權(quán)登錄、注冊(cè)、綁定小程序前端功能實(shí)現(xiàn)。

  微信小程序前端開發(fā)有多種方式,可以使用微信小程序官方開發(fā)方式,也可以使用第三方的開發(fā)方式。因?yàn)榇蠖鄶?shù)前端都會(huì)使用Vue.js開發(fā),而mpvue可以使用開發(fā)Vue.js的方式來(lái)開發(fā)微信小程序,所以這里我們選擇使用mpvue來(lái)開發(fā)微信小程序。這里不詳細(xì)講解mpvue框架的搭建過(guò)程,只詳細(xì)說(shuō)明微信小程序授權(quán)登錄相關(guān)功能,有需要的可以參考mpvue官方文檔。

1、定義微信小程序授權(quán)登錄相關(guān)接口文件login.js,將我們業(yè)務(wù)后臺(tái)實(shí)現(xiàn)的接口統(tǒng)一管理和調(diào)用。

  因?yàn)槲覀兊拈_發(fā)框架是支持多租戶的,同時(shí)也是支持多個(gè)小程序的,為了同一套后臺(tái)可以支持多個(gè)微信小程序,這里選擇在發(fā)布的微信小程序中配置appId,由微信小程序前端參數(shù)來(lái)確定具體的微信小程序。

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)
}

// 獲取用戶手機(jī)號(hào)
export function getUserPhone (params) {
  return fly.get(`/wx/user/${params.appId}/phone`, params)
}

// 綁定微信賬號(hào)
export function bindWeChatUser (params) {
  return fly.get(`/wx/user/${params.appId}/bind`, params)
}

// 解綁微信賬號(hào)
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授權(quán)登錄頁(yè)面,實(shí)現(xiàn)微信小程序具體授權(quán)登錄、綁定、注冊(cè)的操作界面。

  • 增加微信授權(quán)登錄按鈕
    <div class="login-btn" v-show="!showAccountLogin">
      <van-button color="#1aad19"  block open-type="getUserInfo" @getuserinfo="bindGetUserInfo" >微信授權(quán)登錄</van-button>
    </div>
  • 增加微信小程序獲取微信用戶綁定手機(jī)號(hào)的彈出框,應(yīng)微信官方要求,必須由用戶點(diǎn)擊授權(quán)之后,才能夠獲取用戶綁定的手機(jī)號(hào)。
    <van-dialog 
      title="授權(quán)驗(yàn)證"
      @getphonenumber="bindGetUserPhone"
      confirm-button-open-type="getPhoneNumber"
      message="微信一鍵登錄需要綁定您的手機(jī)號(hào)"
      :show="showUserPhoneVisible">
    </van-dialog>
  • 增加賬號(hào)綁定彈出框,同樣微信官方要求,獲取微信用戶信息,必須由用戶點(diǎn)擊授權(quán)允許,所以這里彈出框里的按鈕也是微信用戶同意授權(quán)獲取用戶信息的按鈕。
    <van-dialog 
      title="賬號(hào)綁定"
      @getuserinfo="bindUserInfo"
      confirm-button-open-type="getUserInfo"
      message="登錄成功,是否將賬號(hào)綁定到當(dāng)前微信?"
      :show="showUserInfoVisible">
    </van-dialog>

3、在methods中新增微信綁定相關(guān)方法來(lái)具體實(shí)現(xiàn)微信小程序授權(quán)登錄、綁定、注冊(cè)等操作接口的調(diào)用。

  • 通過(guò)wx.login拿到code,在后臺(tái)通過(guò)openId判斷是否已經(jīng)綁定用戶,如果已綁定用戶,則不需要再進(jìn)行用戶授權(quán)操作,直接登錄.
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、如果綁定過(guò),那么直接使用綁定用戶登錄
              // 2、如果沒(méi)有綁定過(guò),那彈出獲取用戶信息和獲取手機(jī)號(hào)信息進(jìn)行綁定
              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)
            }
          })
        }
      })
    },
  • 獲取微信用戶信息實(shí)現(xiàn)登陸,微信小程序接口,只允許點(diǎn)擊按鈕,用戶同意后才能獲取用戶信息,不要直接使用wx.getUserInfo,此接口已過(guò)期,微信不再支持。
    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、如果綁定過(guò),那么直接使用綁定用戶登錄
          // 2、如果沒(méi)有綁定過(guò),那彈出獲取用戶信息和獲取手機(jī)號(hào)信息進(jìn)行綁定
          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 {
                // 彈出獲取手機(jī)號(hào)授權(quán)按鈕
                that.showUserPhoneVisible = true
              }
            })
          } else {
            // 彈出獲取手機(jī)號(hào)授權(quán)按鈕
            that.showUserPhoneVisible = true
          }
        })
      } else {
        console.log('點(diǎn)擊了拒絕')
      }
    },
  • 微信通過(guò)用戶點(diǎn)擊授權(quán)獲取手機(jī)號(hào)來(lái)實(shí)現(xiàn)賬號(hào)綁定操作,如果用戶點(diǎn)擊拒絕,那么提示用戶無(wú)法綁定當(dāng)前用戶微信綁定的手機(jī)號(hào),請(qǐng)用戶繼續(xù)授權(quán)。
    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('當(dāng)前拒絕授權(quán)手機(jī)號(hào)登陸,請(qǐng)使用賬號(hào)密碼登錄')
      }
    },

  通過(guò)以上開發(fā)基本實(shí)現(xiàn)了微信小程序授權(quán)登錄第三方業(yè)務(wù)系統(tǒng)的功能,在此基礎(chǔ)上,注冊(cè)的功能可以根據(jù)業(yè)務(wù)需求來(lái)擴(kuò)展,大多數(shù)互聯(lián)網(wǎng)業(yè)務(wù),都會(huì)是微信小程序授權(quán)登錄之后就自動(dòng)注冊(cè)用戶。但是有些傳統(tǒng)行業(yè)的業(yè)務(wù),比如只有某些公司或組織內(nèi)部的用戶才能登錄,那么是不允許微信授權(quán)登錄就自助注冊(cè)成系統(tǒng)用戶的。微信小程序前端框架也可以歸根據(jù)自己的需求,及擅長(zhǎng)的開發(fā)方式來(lái)選擇,但是微信授權(quán)登錄的流程是不變的,可以在此基礎(chǔ)上根據(jù)業(yè)務(wù)需求修改優(yōu)化。

分享到:
標(biāo)簽:程序 微信小
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定