步骤1:第三方平台方获取预授权码(pre_auth_code)
预授权码是第三方平台方实现授权托管的必备信息,可以通过本文下文中的获取预授码接口来获取预授权码。
步骤2:引入用户进入授权页
第三方平台方可以在自己的网站:中放置“微信公众号授权”或者“小程序授权”的入口,引导公众号和小程序管理员进入授权页。授权页网址为https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=xxxx&pre_auth_code=xxxxx&redirect_uri=xxxx&auth_type=xxx。
| 参数 | 是否必填 | 参数说明 |
| component_appid | 是 | 第三方平台方appid |
| pre_auth_code | 是 | 预授权码 |
| redirect_uri | 是 | 回调URI |
| auth_type | 否 | 要授权的帐号类型,1则商户扫码后,手机端仅展示公众号、2表示仅展示小程序,3表示公众号和小程序都展示。如果为未制定,则默认小程序和公众号都展示。第三方平台开发者可以使用本字段来控制授权的帐号类型。 |
步骤3:用户确认并同意登录授权给第三方平台方
用户进入第三方平台授权页后,需要确认并同意将自己的公众号或小程序授权给第三方平台方,完成授权流程。
步骤4:授权后回调URI,得到授权码(authorization_code)和过期时间
授权流程完成后,授权页会自动跳转进入回调URI,并在URL参数中返回授权码和过期时间(redirect_url?auth_code=xxx&expires_in=600)
步骤5:利用授权码调用公众号或小程序的相关API
在得到授权码后,第三方平台方可以使用授权码换取授权公众号或小程序的接口调用凭据(authorizer_access_token,也简称为令牌),再通过该接口调用凭据,按照公众号开发者文档或小程序开发文档的说明,去调用公众号或小程序相关API(能调用哪些API,取决于用户将哪些权限集授权给了第三方平台方,也取决于公众号或小程序自身拥有哪些接口权限),使用JS SDK等能力。具体请见【公众号第三方平台的接口说明】
以上是官方的授权流程,在这一步,我们一个很重要的目标就是获取到授权方小程序的accesstoken
实现代码:
- 获取第三方平台component_access_token
@Override
public String getComponentAccessToken(WxAccountName wxAccountName) throws URISyntaxException, BusinessException {
String componentAccessToken = (String) redisService.vGet(CacheUtils.CacheName.APPLETS_COMPONENT_TOKEN, wxAccountName.name());
if (StringUtilPlus.isNotEmpty(componentAccessToken)) {
return componentAccessToken;
}
synchronized (this) {
URI url = new URIBuilder(UrlConstant.POST_API_COMPONENT_TOKEN)
.build();
WxAccountDto accountDto = wxAccountService.getAccountByName(wxAccountName);
AppletsComponentDto appletsComponentDto = new AppletsComponentDto();
appletsComponentDto.setComponentAppid(accountDto.getAppId());
appletsComponentDto.setComponentAppSecret(accountDto.getAppSecret());
//获取票据
appletsComponentDto.setComponentVerifyTicket(accountDto.getTicket());
RequestEntity requestEntity = new RequestEntity<>(appletsComponentDto, HttpMethod.POST, url);
ResponseEntity responseEntity = restTemplate.exchange(requestEntity, AppletsComponentResultDto.class);
AppletsComponentResultDto body = responseEntity.getBody();
if (StringUtilPlus.isNotEmpty(body.getErrcode())) {
LOGGER.error("AppletsServiceImpl getComponentAccessToken fail errorcode [{}] errormsg[{}]", body.getErrcode(), body.getErrmsg());
throw new BusinessException(AppletsConstantModule.ERROR_APPLETS_GET_COMPENENT_ACCESS_TOKEN_ERROR.getCode(), AppletsConstantModule.ERROR_APPLETS_GET_COMPENENT_ACCESS_TOKEN_ERROR.getMessage());
}
componentAccessToken = body.getComponentAccessToken();
//放入缓存
redisService.vPut(CacheUtils.CacheName.APPLETS_COMPONENT_TOKEN, wxAccountName.name(), componentAccessToken);
}
return componentAccessToken;
}
2.获取预授权码
@Override
public String getPreAuthCode(WxAccountName wxAccountName) throws URISyntaxException, BusinessException {
String preAuthCode = (String) redisService.vGet(CacheUtils.CacheName.APPLETS_PRE_AUTH_CODE, wxAccountName.name());
if (StringUtilPlus.isNotEmpty(preAuthCode)) {
return preAuthCode;
}
synchronized (this) {
String componentAccessToken = this.getComponentAccessToken(wxAccountName);
URI url = new URIBuilder(UrlConstant.POST_PRE_AUTH_CODE
.replace("{componentAccessToken}", componentAccessToken))
.build();
WxAccountDto accountDto = wxAccountService.getAccountByName(wxAccountName);
AppletsComponentDto appletsComponentDto = new AppletsComponentDto();
appletsComponentDto.setComponentAppid(accountDto.getAppId());
RequestEntity requestEntity = new RequestEntity<>(appletsComponentDto, HttpMethod.POST, url);
ResponseEntity responseEntity = restTemplate.exchange(requestEntity, AppletsComponentResultDto.class);
AppletsComponentResultDto body = responseEntity.getBody();
if (StringUtilPlus.isNotEmpty(body.getErrcode())) {
LOGGER.error("AppletsServiceImpl getPreAuthCode fail errorcode [{}] errormsg[{}]", body.getErrcode(), body.getErrmsg());
throw new BusinessException(AppletsConstantModule.ERROR_APPLETS_GET_COMPENENT_ACCESS_TOKEN_ERROR.getCode(), AppletsConstantModule.ERROR_APPLETS_GET_COMPENENT_ACCESS_TOKEN_ERROR.getMessage());
}
preAuthCode = body.getPreAuthCode();
//放入缓存
redisService.vPut(CacheUtils.CacheName.APPLETS_PRE_AUTH_CODE, wxAccountName.name(), preAuthCode);
}
return preAuthCode;
}
请看步骤二,https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=xxxx&pre_auth_code=xxxxx&redirect_uri=xxxx&auth_type=xxx
这是一个登陆的链接地址,我们需要将其中的相关信息替换,尤其是redirect_url,授权方在打开这个链接之后会跳转到二维码页面,会重定向到这个url,这个url不需要encode。pre_auth_code为前面获取的预授权码获取授权方token
public Map loginCallback(WxAccountName wxAccountName, String authorizationCode) throws URISyntaxException, BusinessException {
AppletsAuthorizerResultDto.AuthorizationInfo authorizer = this.getAuthorizerAccessToken(wxAccountName, authorizationCode);
String authorizerAppid = authorizer.getAuthorizerAppid();
boolean hasAppId = wxAppletsUserService.hasAppId(authorizerAppid);
Map map = new HashMap<>();
if(hasAppId){
map.put("authorizerAppid", authorizerAppid);
map.put("authorizerAccessToken", authorizer.getAuthorizerAccessToken());
return map;
}
String authorizerAccessToken = authorizer.getAuthorizerAccessToken();
//修改服务器域名
this.modifyDomain(authorizerAccessToken);
//修改业务域名
this.setWebViewDomain(authorizerAccessToken);
//上传代码
this.codeCommit(authorizerAccessToken);
AppletsInfoDto appletsInfo = this.getAppletsInfo(wxAccountName, authorizerAppid);
WxAppletsUserDto wxAppletsUserDto = new WxAppletsUserDto();
wxAppletsUserDto.setAppid(authorizerAppid);
wxAppletsUserDto.setAppletsName(appletsInfo.getAuthorizerInfo().getNickName());
wxAppletsUserDto.setOriginalId(appletsInfo.getAuthorizerInfo().getUserName());
wxAppletsUserDto.setAuthorized(false);
wxAppletsUserService.insertWxAppletsUser(wxAppletsUserDto);
map.put("authorizerAppid", authorizer.getAuthorizerAppid());
map.put("authorizerAccessToken", authorizer.getAuthorizerAccessToken());
return map;
}
@Override
public AppletsAuthorizerResultDto.AuthorizationInfo getAuthorizerAccessToken(WxAccountName wxAccountName, String authorizationCode) throws URISyntaxException, BusinessException {
String componentAccessToken = this.getComponentAccessToken(wxAccountName);
URI url = new URIBuilder(UrlConstant.POST_API_QUERY_AUTH
.replace("{componentAccessToken}", componentAccessToken))
.build();
WxAccountDto accountDto = wxAccountService.getAccountByName(wxAccountName);
AppletsComponentDto appletsComponentDto = new AppletsComponentDto();
appletsComponentDto.setComponentAppid(accountDto.getAppId());
appletsComponentDto.setAuthorizationCode(authorizationCode);
RequestEntity requestEntity = new RequestEntity<>(appletsComponentDto, HttpMethod.POST, url);
ResponseEntity responseEntity = restTemplate.exchange(requestEntity, AppletsAuthorizerResultDto.class);
AppletsAuthorizerResultDto body = responseEntity.getBody();
if (StringUtilPlus.isNotEmpty(body.getErrcode())) {
LOGGER.error("AppletsServiceImpl getAuthorizerAccessToken fail errorcode [{}] errormsg[{}]", body.getErrcode(), body.getErrmsg());
throw new BusinessException(AppletsConstantModule.ERROR_APPLETS_GET_AUTHORIZER_ACCESS_TOKEN_ERROR.getCode(), AppletsConstantModule.ERROR_APPLETS_GET_AUTHORIZER_ACCESS_TOKEN_ERROR.getMessage());
}
AppletsAuthorizerResultDto.AuthorizationInfo authorizationInfo = body.getAuthorizationInfo();
return authorizationInfo;
}
@Override
public AppletsInfoDto getAppletsInfo(WxAccountName wxAccountName, String authorizerAppid) throws BusinessException, URISyntaxException {
WxAccountDto accountDto = wxAccountService.getAccountByName(wxAccountName);
String componentAccessToken = getComponentAccessToken(wxAccountName);
URI url = new URIBuilder(UrlConstant.POST_AUTHPRIZER_INFO
.replace("{accessToken}", componentAccessToken))
.build();
AppletsComponentDto appletsComponentDto = new AppletsComponentDto();
appletsComponentDto.setComponentAppid(accountDto.getAppId());
appletsComponentDto.setAuthorizerAppid(authorizerAppid);
RequestEntity requestEntity = new RequestEntity<>(appletsComponentDto, HttpMethod.POST, url);
ResponseEntity responseEntity = restTemplate.exchange(requestEntity, AppletsInfoDto.class);
AppletsInfoDto body = responseEntity.getBody();
if (StringUtilPlus.isNotEmpty(body.getErrcode()) && !"0".equals(body.getErrcode())) {
LOGGER.error("AppletsServiceImpl getAppletsInfo fail errorcode [{}] errormsg[{}]", body.getErrcode(), body.getErrmsg());
throw new BusinessException(AppletsConstantModule.ERROR_APPLETS_GET_INFO_FAIL.getCode(), AppletsConstantModule.ERROR_APPLETS_GET_INFO_FAIL.getMessage());
}
return body;
}
@Override
public void modifyDomain(String authorizerAccessToken) throws URISyntaxException, BusinessException {
URI url = new URIBuilder(UrlConstant.POST_MODIFY_DOMAIN
.replace("{accessToken}", authorizerAccessToken))
.build();
AppletsManagerDto appletsManagerDto = new AppletsManagerDto();
appletsManagerDto.setAction("set");
List domainList = new ArrayList<>();
domainList.add("https://sjptdevtest.mynatapp.cc");
appletsManagerDto.setRequestdomain(domainList);
appletsManagerDto.setDownloaddomain(domainList);
appletsManagerDto.setUploaddomain(domainList);
appletsManagerDto.setWsrequestdomain(domainList);
RequestEntity requestEntity = new RequestEntity<>(appletsManagerDto, HttpMethod.POST, url);
ResponseEntity responseEntity = restTemplate.exchange(requestEntity, AppletsAuthorizerResultDto.class);
AppletsAuthorizerResultDto body = responseEntity.getBody();
if (StringUtilPlus.isNotEmpty(body.getErrcode()) && !"0".equals(body.getErrcode())) {
LOGGER.error("AppletsServiceImpl modifyDomain fail errorcode [{}] errormsg[{}]", body.getErrcode(), body.getErrmsg());
throw new BusinessException(AppletsConstantModule.ERROR_APPLETS_MODIFY_DOMAIN_ERROR.getCode(), AppletsConstantModule.ERROR_APPLETS_MODIFY_DOMAIN_ERROR.getMessage());
}
}
@Override
public void setWebViewDomain(String authorizerAccessToken) throws URISyntaxException, BusinessException {
URI url = new URIBuilder(UrlConstant.POST_SET_WEBVIEW_DOMAIN
.replace("{accessToken}", authorizerAccessToken))
.build();
HttpEntity formEntity = new HttpEntity("{}");
AppletsAuthorizerResultDto body = restTemplate.postForObject(url, formEntity, AppletsAuthorizerResultDto.class);
}
}
@Override
public void codeCommit(String authorizerAccessToken) throws URISyntaxException, BusinessException {
URI url = new URIBuilder(UrlConstant.POST_CODE_COMMIT
.replace("{accessToken}", authorizerAccessToken))
.build();
AppletsManagerDto appletsManagerDto = new AppletsManagerDto();
appletsManagerDto.setTemplateId(2L);
appletsManagerDto.setExtJson("{}");
appletsManagerDto.setUserDesc("test");
appletsManagerDto.setUserVersion("V1.0");
RequestEntity requestEntity = new RequestEntity<>(appletsManagerDto, HttpMethod.POST, url);
ResponseEntity responseEntity = restTemplate.exchange(requestEntity, AppletsAuthorizerResultDto.class);
AppletsAuthorizerResultDto body = responseEntity.getBody();
if (StringUtilPlus.isNotEmpty(body.getErrcode()) && !"0".equals(body.getErrcode())) {
LOGGER.error("AppletsServiceImpl codeCommit fail errorcode [{}] errormsg[{}]", body.getErrcode(), body.getErrmsg());
throw new BusinessException(AppletsConstantModule.ERROR_APPLETS_CODE_COMMIT_FAIL.getCode(), AppletsConstantModule.ERROR_APPLETS_CODE_COMMIT_FAIL.getMessage());
}
}
关键步骤:
//修改服务器域名
this.modifyDomain(authorizerAccessToken);
//修改业务域名
this.setWebViewDomain(authorizerAccessToken);
//上传代码
this.codeCommit(authorizerAccessToken);
这些都应该在授权方第一次授权时,将授权方的服务器域名和业务域名都设置未我们第三方的域名。
友情提示: 微信有个很大的坑!有些post请求并没有参数,但是你还是必须得传一个{} 空的json值过去,虽然我也不知道意义何在!不然就是empty post