NO.2 微信第三方平台(小程序)授权流程技术说明

官方文档

最好先熟悉以上文档

正文:

1: 授权流程

在这里插入图片描述

1、推送component_verify_ticket协议

在第三方平台创建审核通过后,微信服务器会向其“授权事件接收URL”每隔10分钟定时推送component_verify_ticket。第三方平台方在收到ticket推送后也需进行解密(详细请见【消息加解密】),接收到后必须直接返回字符串success。

   /**
     * 接收ticket 微信每10分钟推送一次
     */
    @PostMapping("ticket")
    public String ticket(HttpServletRequest request) throws Exception {
    
    
        log.info("接收ticket==================》开始");
        String msgSignature = request.getParameter("msg_signature");
        String timeStamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");

        log.info("开始接受postdata---" + request.getInputStream());
        BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
        StringBuffer sb = new StringBuffer();
        String line = null;
        while ((line = br.readLine()) != null) {
    
    
            sb = sb.append(line);
        }
        String postData = sb.toString();
        log.info(msgSignature + "==============》" + timeStamp + "==============》" + nonce + "==============》" + postData);
        WXBizMsgCrypt pc = new WXBizMsgCrypt(WeiXinConstant.TOKEN, WeiXinConstant.ENCODING_AESKEY, WeiXinConstant.THREE_APP_ID);
        String result = pc.decryptMsg(msgSignature, timeStamp, nonce, postData);
        //appId 第三方平台APPid;
        // InfoType component_verify_ticket;
        // ComponentVerifyTicket Ticket内容
        log.info(result + ".................");
        //保存最新的ticket为10分钟,微信每十分钟会掉一次该接口
        Map map = XmlToMapUtil.toMap(result);
        if (CollectionUtils.isEmpty(map)) {
    
    
            log.info("map===================>null【{}】", JSON.toJSONString(map));
            return "";
        }
        log.info(map.get("ComponentVerifyTicket").toString() + "》=============================");
        redisUtil.set("ticket", map.get("ComponentVerifyTicket").toString(), 10 * 60 * 1000);
        return "success";
    }

2、获取第三方平台component_access_token

注意:第三方平台component_access_token是第三方平台的下文中接口的调用凭据,也叫做令牌(component_access_token)。每个令牌是存在有效期(2小时)的,且令牌的调用不是无限制的,请第三方平台做好令牌的管理,在令牌快过期时(比如1小时50分)再进行刷新。

    @GetMapping("getComponentAccessToken")
    @ApiOperation("获取第三方平台component_access_token")
    public ApiResponse getComponentAccessToken() {
    
    
        //测试用
        String componentVverifyTicket = String.valueOf(redisUtil.get("ticket"));
        log.info("获取第三方平台component_access_token==========》componentVverifyTicket【{}】", componentVverifyTicket);
        Map parampMap = new HashMap();
        //component_appid	第三方平台appid
        parampMap.put("component_appid", WeiXinConstant.THREE_APP_ID);
        //component_appsecret	第三方平台appsecret
        parampMap.put("component_appsecret", WeiXinConstant.THREE_APP_SCREPT);
        //微信后台推送的ticket,此ticket会定时推送,即 步骤1里的值
        parampMap.put("component_verify_ticket", componentVverifyTicket);
        String apiComponentToken = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
        ResponseEntity<String> entity = restTemplate.postForEntity(apiComponentToken, parampMap, String.class);
        
        if (entity.getStatusCode().equals(HttpStatus.OK)) {
    
    
            entity.getBody();
            log.info("获取第三方平台component_access_token==========》【{}】", entity.getBody());
            //保存最新的
            redisUtil.set("component_access_token", JSON.parseObject(entity.getBody()).getString("component_access_token"));

        } else {
    
    
            log.error("获取第三方平台component_access_token==========》失败!");
        }
        return ResponseUtil.success(entity.getBody());
    }

正确返回结果:

{
    
    "component_access_token":"61W3mEpU66027wgNZ_MhGHNQDHnFATkDa9-2llqrMBjUwxRSNPbVsMmyD-yq8wZETSoE5NQgecigDrSHkPtIYA", "expires_in":7200}

有效期为7200s 如需长期使用,缓存起来,在未到期之前提前刷新该值

3,获取预授权码pre_auth_code

    @GetMapping("getPreAuthCode")
    @ApiOperation("通过ComponentAccessToken获取第三方预授权码")
    public ApiResponse getPreAuthCode() {
    
    
        //测试
        String componentAccessToken = String.valueOf(redisUtil.get("component_access_token"));
        log.error("获取预授权码pre_auth_code==========》开始!");
        Map parampMap = new HashMap();
        //WeiXinConstant.THREE_APP_ID 第三方平台的APPID
        parampMap.put("component_appid", WeiXinConstant.THREE_APP_ID);
        StringBuffer stf = new StringBuffer("https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode");
        //componentAccessToken 步骤2的值
        stf.append("?component_access_token=").append(componentAccessToken);
        ResponseEntity<String> entity = restTemplate.postForEntity(stf.toString(), parampMap, String.class);
        if (entity.getStatusCode().equals(HttpStatus.OK)) {
    
    
            entity.getBody();
            log.info("获取预授权码pre_auth_code==========【{}】", entity.getBody());
            //每次存入最新的
            redisUtil.set("pre_auth_code", JSON.parseObject(entity.getBody()).getString("pre_auth_code"), 600);
        } else {
    
    
            log.error("获取预授权码pre_auth_code==========》失败!");
        }
        return ResponseUtil.success(entity.getBody());
    }

正确返回:

{
    
    "pre_auth_code":"Cx_Dk6qiBE0Dmx4EmlT3oRfArPvwSQ-oa3NL_fwHM7VI08r52wazoZX2Rhpz1dEw","expires_in":600}

4,使用授权码换取公众号或小程序的接口调用凭据和授权信息

注意注意注意:此步骤很重要,4.1 和4.2 选一种参考即可,推荐4.2

网页授权发起的请求需要和在三方平台上的填写的域名一样才可以,否则会报域名发起错误

4.1.授权注册页面扫码授权

    @GetMapping("getAuthorizationCodeByPC")
    @ApiOperation("获取授权页面")
    public ApiResponse getAuthorizationCodeByPC() {
    
    
        log.error(" 获取授权页面=========》开始!");
        String preauthcode = String.valueOf(redisUtil.get("pre_auth_code"));
        StringBuffer stf = new StringBuffer("https://mp.weixin.qq.com/cgi-bin/componentloginpage?");
        stf.append("component_appid=").append(WeiXinConstant.THREE_APP_ID);
        stf.append("&pre_auth_code=").append(preauthcode);
        stf.append("&redirect_uri=").append("http://zhaojunjie.nat300.top/receive/authorizationCode");
        log.info("请求页面【{}】", stf.toString());
        ResponseEntity<String> entity = restTemplate.getForEntity(stf.toString(), String.class);
        if (entity.getStatusCode().equals(HttpStatus.OK)) {
    
    
            log.info(" 获取授权页面=========》SUCCESS");
        } else {
    
    
            log.error(" 获取授权页面=========》失败!");
        }
        return ResponseUtil.success(entity.getBody());
    }

4.2.点击移动端链接快速授权

    @GetMapping("getAuthorizationCodeByAp")
    @ApiOperation("获取授权链接移动端")
    public ApiResponse getAuthorizationCodeByAp() {
    
    
        log.error("授权链接移动端=========》开始!");
        String preauthcode = String.valueOf(redisUtil.get("pre_auth_code"));
        StringBuffer stf = new StringBuffer("https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&auth_type=3&no_scan=1");
        stf.append("&component_appid=").append(WeiXinConstant.THREE_APP_ID);
        stf.append("&pre_auth_code=").append(preauthcode);
        stf.append("&redirect_uri=").append("https://jikezhan.com/manager/receive/authorizationCode");
        stf.append("&auth_type=").append("2").append("#wechat_redirect");
        log.info("授权链接移动端【{}】", stf.toString());
        ResponseEntity<String> entity = restTemplate.getForEntity(stf.toString(), String.class);
        if (entity.getStatusCode().equals(HttpStatus.OK)) {
    
    
            log.info(" 授权链接移动端=========》SUCCESS");
        } else {
    
    
            log.error(" 授权链接移动端=========》失败!");
        }
        return ResponseUtil.success(entity.getBody());
    }

两种授权方式选择一种测试就行,第一种生成的网页要在设置的域名下认证。所以推荐第二种,移动端授权生成的链接直接发送给授权方授权即可,授权成功后会收到微信的通知,回调接口如下:

   /**
     * 用户扫码授权后的回调
     * 授权成功后会返回授权code
     */
    @GetMapping("authorizationCode")
    public String authorizationCode(HttpServletRequest request) {
    
    
        log.info("预授权码换取授权码回调接口=========》开始【{}】", JSON.toJSONString(request.getParameterMap()));
        //{"auth_code":["queryauthcode@@@jD3ANR367yXFce3z-     8ALAh_lQ4Tmr_sPyLRf20SKVwmv1UpnVVqp22FEWLv5_mSu1BKEAX3zKFrB9yW3P8fLew"],"expires_in":["3600"]}
        //授权code,会在授权成功时返回给第三方平台
        log.info("授权码======================》【{}】", request.getParameterMap().get("auth_code"));
        String[] authCode = request.getParameterMap().get("auth_code");
        redisUtil.set("authorizationCode", StringUtils.join(authCode), 7200);
        return "success";
    }

同样是有时效性的

4.3
该API用于使用授权码换取授权公众号或小程序的授权信息,并换取authorizer_access_token和authorizer_refresh_token。 授权码的获取,需要在用户在第三方平台授权页中完成授权流程后,在回调URI中通过URL参数提供给第三方平台方。请注意,由于现在公众号或小程序可以自定义选择部分权限授权给第三方平台,因此第三方平台开发者需要通过该接口来获取公众号或小程序具体授权了哪些权限,而不是简单地认为自己声明的权限就是公众号或小程序授权的权限。

    @GetMapping("apiQueryAuth")
    public ApiResponse apiQueryAuth() {
    
    
        log.info("使用授权码换取公众号或小程序的接口调用凭据和授权信息=========》开始");
        String authorizationCode = String.valueOf(redisUtil.get("authorizationCode"));
        log.info("authorizationCode========================》【{}】", authorizationCode);
        String componentAccessToken = String.valueOf(redisUtil.get("component_access_token"));
        log.info("componentAccessToken========================》【{}】", componentAccessToken);

        Map parampMap = new HashMap();
        parampMap.put("component_appid", WeiXinConstant.THREE_APP_ID);
        //authorization_code 即上述4.1 或4.2用户授权后微信推送给我们的
        parampMap.put("authorization_code", authorizationCode);
        StringBuffer stf = new StringBuffer("https://api.weixin.qq.com/cgi-bin/component/api_query_auth");
        stf.append("?component_access_token=").append(componentAccessToken);
        log.info("链接【{}】", stf.toString());
        ResponseEntity<String> entity = restTemplate.postForEntity(stf.toString(), parampMap, String.class);
        if (entity.getStatusCode().equals(HttpStatus.OK)) {
    
    
            JSONObject result = JSON.parseObject(entity.getBody());
            JSONObject authorizationInfo = (JSONObject) result.get("authorization_info");
            //授权的APPID
            String authorizerAppid = authorizationInfo.getString("authorizer_appid");
            redisUtil.set("authorizer_appid", authorizerAppid);
            //authorizer_access_token
            String authorizerAccessToken = authorizationInfo.getString("authorizer_access_token");
            redisUtil.set("authorizer_access_token", authorizerAccessToken, 7200);
            //刷新token
            String authorizerRefreshToken = authorizationInfo.getString("authorizer_refresh_token");
            redisUtil.set("authorizer_refresh_token", authorizerRefreshToken);
            log.info("使用授权码换取公众号或小程序的接口调用凭据和授权信息=========》【{}】", entity.getBody());
        } else {
    
    
            log.error("使用授权码换取公众号或小程序的接口调用凭据和授权信息=========》失败!");
        }
        return ResponseUtil.success(entity.getBody());
    }

返回结果示例

{
    
    
"authorization_info": {
    
    
"authorizer_appid": "wxf8b4f85f3a794e77",
"authorizer_access_token": "QXjUqNqfYVH0yBE1iI_7vuN_9gQbpjfK7hYwJ3P7xOa88a89-Aga5x1NMYJyB8G2yKt1KCl0nPC3W9GJzw0Zzq_dBxc8pxIGUNi_bFes0qM",
"expires_in": 7200,
"authorizer_refresh_token": "dTo-YCXPL4llX-u1W1pPpnp8Hgm4wpJtlR6iV0doKdY",
"func_info": [
{
    
    
"funcscope_category": {
    
    
"id": 1
}
},
{
    
    
"funcscope_category": {
    
    
"id": 2
}
},
{
    
    
"funcscope_category": {
    
    
"id": 3
}
}
]
}}

返回参数:
authorization_info 授权信息
authorizer_appid 授权方appid
authorizer_access_token 授权方接口调用凭据(在授权的公众号或小程序具备API权限时,才有此返回值),也简称为令牌
expires_in 有效期(在授权的公众号或小程序具备API权限时,才有此返回值)
authorizer_refresh_token
接口调用凭据刷新令牌(在授权的公众号具备API权限时,才有此返回值),刷新令牌主要用于第三方平台获取和刷新已授权用户的access_token,只会在授权时刻提供,请妥善保存。 一旦丢失,只能让用户重新授权,才能再次拿到新的刷新令牌


authorizer_access_token 有时效性,在后续的接口会经常用到,所以尽量在到期之前提前使用 authorizer_refresh_token
刷新 authorizer_access_token代码如下:

    @GetMapping("authorizerRefreshToken")
    @ApiOperation("获取(刷新)授权公众号或小程序的接口调用凭据(令牌)")
    public ApiResponse authorizerRefreshToken() {
    
    
        String componentAccessToken = String.valueOf(redisUtil.get("component_access_token"));
        StringBuffer stf = new StringBuffer("https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=");
        stf.append(componentAccessToken);
        Map<String, String> parmMap = new HashMap();
        String authorizerRefreshToken = redisUtil.get("authorizer_refresh_token") == null ? "" : String.valueOf(redisUtil.get("authorizer_refresh_token"));
        parmMap.put("component_appid", WeiXinConstant.THREE_APP_ID);
        parmMap.put("authorizer_appid", String.valueOf(redisUtil.get("authorizer_appid")));
        parmMap.put("authorizer_refresh_token", authorizerRefreshToken);
        ResponseEntity<String> entity = restTemplate.postForEntity(stf.toString(), parmMap, String.class);
        if (entity.getStatusCode().equals(HttpStatus.OK)) {
    
    
            JSONObject result = JSONObject.parseObject(entity.getBody());
            String authorizerAccessToken = result.getString("authorizer_access_token");
            redisUtil.set("authorizer_access_token", authorizerAccessToken, 7200);
            String newAuthorizerRefreshToken = result.getString("authorizer_refresh_token");
            redisUtil.set("authorizer_refresh_token", newAuthorizerRefreshToken);
            log.info("获取(刷新)授权公众号或小程序的接口调用凭据(令牌)=========》【{}】", entity.getBody());
        } else {
    
    
            log.error("获取(刷新)授权公众号或小程序的接口调用凭据(令牌)=========》失败!");
        }
        return ResponseUtil.success(entity.getBody());
    }

本人QQ:571726193

群(新 人少) :1030289715

欢迎来交流,什么问题也欢迎指出。

猜你喜欢

转载自blog.csdn.net/zhaojunjie_cc/article/details/96476225