* 重新实现企业微信三方授权,获取永久授权码返回信息 * 1、增加获取服务商三方预授权URL 2、修复授权后应用图标字段为空的bug Co-authored-by: 袁启勋 <xun.yuan@infoship.cn>dev1
| @@ -7,6 +7,7 @@ import me.chanjar.weixin.common.util.http.RequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.RequestHttp; | import me.chanjar.weixin.common.util.http.RequestHttp; | ||||
| import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | ||||
| import me.chanjar.weixin.cp.bean.WxCpTpCorp; | import me.chanjar.weixin.cp.bean.WxCpTpCorp; | ||||
| import me.chanjar.weixin.cp.bean.WxCpTpPermanentCodeInfo; | |||||
| import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | ||||
| /** | /** | ||||
| @@ -88,8 +89,36 @@ public interface WxCpTpService { | |||||
| * @param authCode . | * @param authCode . | ||||
| * @return . | * @return . | ||||
| */ | */ | ||||
| @Deprecated | |||||
| WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException; | WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException; | ||||
| /** | |||||
| * 获取企业永久授权码信息 | |||||
| * <pre> | |||||
| * 原来的方法实现不全 | |||||
| * </pre> | |||||
| * | |||||
| * @param authCode | |||||
| * @return | |||||
| * | |||||
| * @author yuan | |||||
| * @since 2020-03-18 | |||||
| * | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| WxCpTpPermanentCodeInfo getPermanentCodeInfo(String authCode) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 获取预授权链接 | |||||
| * </pre> | |||||
| * @param redirectUri 授权完成后的回调网址 | |||||
| * @param state a-zA-Z0-9的参数值(不超过128个字节),用于第三方自行校验session,防止跨域攻击 | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| String getPreAuthUrl(String redirectUri,String state) throws WxErrorException; | |||||
| /** | /** | ||||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. | * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. | ||||
| * | * | ||||
| @@ -3,6 +3,7 @@ package me.chanjar.weixin.cp.api.impl; | |||||
| import com.google.common.base.Joiner; | import com.google.common.base.Joiner; | ||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
| import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
| import lombok.SneakyThrows; | |||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
| import me.chanjar.weixin.common.WxType; | import me.chanjar.weixin.common.WxType; | ||||
| import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
| @@ -17,10 +18,14 @@ import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||||
| import me.chanjar.weixin.cp.api.WxCpTpService; | import me.chanjar.weixin.cp.api.WxCpTpService; | ||||
| import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | ||||
| import me.chanjar.weixin.cp.bean.WxCpTpCorp; | import me.chanjar.weixin.cp.bean.WxCpTpCorp; | ||||
| import me.chanjar.weixin.cp.bean.WxCpTpPermanentCodeInfo; | |||||
| import me.chanjar.weixin.cp.bean.WxCpTpPreauthCode; | |||||
| import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | ||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import java.io.File; | import java.io.File; | ||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.net.URLEncoder; | |||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.Map; | import java.util.Map; | ||||
| @@ -123,6 +128,26 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ | |||||
| return wxCpTpCorp; | return wxCpTpCorp; | ||||
| } | } | ||||
| @Override | |||||
| public WxCpTpPermanentCodeInfo getPermanentCodeInfo(String authCode) throws WxErrorException{ | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| jsonObject.addProperty("auth_code", authCode); | |||||
| String result = post(configStorage.getApiUrl(GET_PERMANENT_CODE), jsonObject.toString()); | |||||
| return WxCpTpPermanentCodeInfo.fromJson(result); | |||||
| } | |||||
| @Override | |||||
| @SneakyThrows | |||||
| public String getPreAuthUrl(String redirectUri,String state) throws WxErrorException{ | |||||
| String result = get(configStorage.getApiUrl(GET_PREAUTH_CODE),null); | |||||
| WxCpTpPreauthCode preauthCode = WxCpTpPreauthCode.fromJson(result); | |||||
| String preAuthUrl = "https://open.work.weixin.qq.com/3rdapp/install?suite_id="+configStorage.getSuiteId()+ | |||||
| "&pre_auth_code="+preauthCode.getPreAuthCode()+"&redirect_uri="+ URLEncoder.encode(redirectUri,"utf-8"); | |||||
| if(StringUtils.isNotBlank(state)) | |||||
| preAuthUrl += "&state="+state; | |||||
| return preAuthUrl; | |||||
| } | |||||
| @Override | @Override | ||||
| public String get(String url, String queryParam) throws WxErrorException { | public String get(String url, String queryParam) throws WxErrorException { | ||||
| return execute(SimpleGetRequestExecutor.create(this), url, queryParam); | return execute(SimpleGetRequestExecutor.create(this), url, queryParam); | ||||
| @@ -0,0 +1,220 @@ | |||||
| package me.chanjar.weixin.cp.bean; | |||||
| import com.google.gson.annotations.SerializedName; | |||||
| import lombok.Getter; | |||||
| import lombok.Setter; | |||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
| import java.util.List; | |||||
| /** | |||||
| * 微信部门. | |||||
| * | |||||
| * @author Daniel Qian | |||||
| */ | |||||
| @Getter | |||||
| @Setter | |||||
| public class WxCpTpPermanentCodeInfo extends WxCpBaseResp { | |||||
| private static final long serialVersionUID = -5028321625140879571L; | |||||
| @SerializedName("access_token") | |||||
| private String accessToken; | |||||
| @SerializedName("expires_in") | |||||
| private Long ExpiresIn; | |||||
| @SerializedName("permanent_code") | |||||
| private String permanentCode; | |||||
| /** | |||||
| * 授权企业信息 | |||||
| */ | |||||
| @SerializedName("auth_corp_info") | |||||
| private AuthCorpInfo authCorpInfo; | |||||
| /** | |||||
| * 授权信息。如果是通讯录应用,且没开启实体应用,是没有该项的。通讯录应用拥有企业通讯录的全部信息读写权限 | |||||
| */ | |||||
| @SerializedName("auth_info") | |||||
| private AuthInfo authInfo; | |||||
| /** | |||||
| * 授权用户信息 | |||||
| */ | |||||
| @SerializedName("auth_user_info") | |||||
| private AuthUserInfo authUserInfo; | |||||
| @Getter | |||||
| @Setter | |||||
| public static class AuthCorpInfo { | |||||
| @SerializedName("corpid") | |||||
| private String corpId; | |||||
| @SerializedName("corp_name") | |||||
| private String corpName; | |||||
| @SerializedName("corp_type") | |||||
| private String corpType; | |||||
| @SerializedName("corp_square_logo_url") | |||||
| private String corpSquareLogoUrl; | |||||
| @SerializedName("corp_round_logo_url") | |||||
| private String corpRoundLogoUrl; | |||||
| @SerializedName("corp_user_max") | |||||
| private String corpUserMax; | |||||
| @SerializedName("corp_agent_max") | |||||
| private String corpAgentMax; | |||||
| /** | |||||
| * 所绑定的企业微信主体名称(仅认证过的企业有) | |||||
| */ | |||||
| @SerializedName("corp_full_name") | |||||
| private String corpFullName; | |||||
| /** | |||||
| * 认证到期时间 | |||||
| */ | |||||
| @SerializedName("verified_end_time") | |||||
| private Long verifiedEndTime; | |||||
| /** | |||||
| * 企业类型,1. 企业; 2. 政府以及事业单位; 3. 其他组织, 4.团队号 | |||||
| */ | |||||
| @SerializedName("subject_type") | |||||
| private Integer subjectType; | |||||
| /** | |||||
| * 授权企业在微工作台(原企业号)的二维码,可用于关注微工作台 | |||||
| */ | |||||
| @SerializedName("corp_wxqrcode") | |||||
| private String corpWxqrcode; | |||||
| @SerializedName("corp_scale") | |||||
| private String corpScale; | |||||
| @SerializedName("corp_industry") | |||||
| private String corpIndustry; | |||||
| @SerializedName("corp_sub_industry") | |||||
| private String corpSubIndustry; | |||||
| @SerializedName("location") | |||||
| private String location; | |||||
| } | |||||
| /** | |||||
| * 授权信息 | |||||
| */ | |||||
| @Getter | |||||
| @Setter | |||||
| public static class AuthInfo { | |||||
| /** | |||||
| * 授权的应用信息,注意是一个数组,但仅旧的多应用套件授权时会返回多个agent,对新的单应用授权,永远只返回一个agent | |||||
| */ | |||||
| @SerializedName("agent") | |||||
| private List<Agent> agent; | |||||
| } | |||||
| @Getter | |||||
| @Setter | |||||
| public static class Agent { | |||||
| @SerializedName("agentid") | |||||
| private Integer agentid; | |||||
| @SerializedName("name") | |||||
| private String name; | |||||
| @SerializedName("round_logo_url") | |||||
| private String roundLogoUrl; | |||||
| @SerializedName("square_logo_url") | |||||
| private String squareLogoUrl; | |||||
| /** | |||||
| * 旧的多应用套件中的对应应用id,新开发者请忽略 | |||||
| */ | |||||
| @SerializedName("appid") | |||||
| @Deprecated | |||||
| private String appid; | |||||
| /** | |||||
| * 应用权限 | |||||
| */ | |||||
| @SerializedName("privilege") | |||||
| private Privilege privilege; | |||||
| } | |||||
| /** | |||||
| * 授权人员信息 | |||||
| */ | |||||
| @Getter | |||||
| @Setter | |||||
| public static class AuthUserInfo { | |||||
| @SerializedName("userid") | |||||
| private String userid; | |||||
| @SerializedName("name") | |||||
| private String name; | |||||
| @SerializedName("avatar") | |||||
| private String avatar; | |||||
| } | |||||
| /** | |||||
| * 应用对应的权限 | |||||
| */ | |||||
| @Getter | |||||
| @Setter | |||||
| public static class Privilege { | |||||
| /** | |||||
| * 权限等级。 | |||||
| * 1:通讯录基本信息只读 | |||||
| * 2:通讯录全部信息只读 | |||||
| * 3:通讯录全部信息读写 | |||||
| * 4:单个基本信息只读 | |||||
| * 5:通讯录全部信息只写 | |||||
| */ | |||||
| @SerializedName("level") | |||||
| private Integer level; | |||||
| @SerializedName("allow_party") | |||||
| private List<Integer> allowParty; | |||||
| @SerializedName("allow_user") | |||||
| private List<String> allowUser; | |||||
| @SerializedName("allow_tag") | |||||
| private List<Integer> allowTag; | |||||
| @SerializedName("extra_party") | |||||
| private List<Integer> extraParty; | |||||
| @SerializedName("extra_user") | |||||
| private List<String> extraUser; | |||||
| @SerializedName("extra_tag") | |||||
| private List<Integer> extraTag; | |||||
| } | |||||
| public static WxCpTpPermanentCodeInfo fromJson(String json) { | |||||
| return WxCpGsonBuilder.create().fromJson(json, WxCpTpPermanentCodeInfo.class); | |||||
| } | |||||
| public String toJson() { | |||||
| return WxCpGsonBuilder.create().toJson(this); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| package me.chanjar.weixin.cp.bean; | |||||
| import com.google.gson.annotations.SerializedName; | |||||
| import lombok.Getter; | |||||
| import lombok.Setter; | |||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
| /** | |||||
| * 预授权码返回 | |||||
| * @author yqx | |||||
| * @date 2020/3/19 | |||||
| */ | |||||
| @Getter | |||||
| @Setter | |||||
| public class WxCpTpPreauthCode extends WxCpBaseResp { | |||||
| @SerializedName("pre_auth_code") | |||||
| String preAuthCode; | |||||
| @SerializedName("expires_in") | |||||
| Long expiresIn; | |||||
| public static WxCpTpPreauthCode fromJson(String json) { | |||||
| return WxCpGsonBuilder.create().fromJson(json, WxCpTpPreauthCode.class); | |||||
| } | |||||
| } | |||||
| @@ -93,6 +93,7 @@ public final class WxCpApiPathConsts { | |||||
| public static final String GET_PERMANENT_CODE = "/cgi-bin/service/get_permanent_code"; | public static final String GET_PERMANENT_CODE = "/cgi-bin/service/get_permanent_code"; | ||||
| public static final String GET_SUITE_TOKEN = "/cgi-bin/service/get_suite_token"; | public static final String GET_SUITE_TOKEN = "/cgi-bin/service/get_suite_token"; | ||||
| public static final String GET_PROVIDER_TOKEN = "/cgi-bin/service/get_provider_token"; | public static final String GET_PROVIDER_TOKEN = "/cgi-bin/service/get_provider_token"; | ||||
| public static final String GET_PREAUTH_CODE = "/cgi-bin/service/get_pre_auth_code"; | |||||
| } | } | ||||
| public static class User { | public static class User { | ||||
| @@ -5,6 +5,7 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
| import me.chanjar.weixin.cp.api.WxCpTpService; | import me.chanjar.weixin.cp.api.WxCpTpService; | ||||
| import me.chanjar.weixin.cp.bean.WxCpTpCorp; | import me.chanjar.weixin.cp.bean.WxCpTpCorp; | ||||
| import me.chanjar.weixin.cp.bean.WxCpTpPermanentCodeInfo; | |||||
| import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | ||||
| import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl; | import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl; | ||||
| import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | ||||
| @@ -128,6 +129,71 @@ public class BaseWxCpTpServiceImplTest { | |||||
| final WxCpTpCorp tpCorp = tpService.getPermanentCode(authCode); | final WxCpTpCorp tpCorp = tpService.getPermanentCode(authCode); | ||||
| assertThat(tpCorp.getPermanentCode()).isEqualTo("xxxx"); | assertThat(tpCorp.getPermanentCode()).isEqualTo("xxxx"); | ||||
| final WxCpTpPermanentCodeInfo tpPermanentCodeInfo = tpService.getPermanentCodeInfo(authCode); | |||||
| assertThat(tpPermanentCodeInfo.getAuthInfo().getAgent().get(0).getAgentid()).isEqualTo(1); | |||||
| } | |||||
| @Test | |||||
| public void testGetPermanentCodeInfo() throws WxErrorException{ | |||||
| String returnJson = "{\n" + | |||||
| " \"access_token\": \"u6SoEWyrEmworJ1uNzddbiXh42mCLNU_mdd6b01Afo2LKmyi-WdaaYqhEGFZjB1RGZ-rhjLcAJ86ger7b7Q0gowSw9iIDR8dm49aVH_MztzmQttP3XFG7np1Dxs_VQkVwhhRmfRpEonAmK1_JWIFqayJXXiPUS3LsFd3tWpE7rxmsRa7Ev2ml2htbRp_qGUjtFTErKoDsnNGSka6_RqFPA\", \n" + | |||||
| " \"expires_in\": 7200, \n" + | |||||
| " \"permanent_code\": \"lMLlxss77ntxzuEl1i1_AQ3-6-cvqMLYs209YNWVruk\", \n" + | |||||
| " \"auth_corp_info\": {\n" + | |||||
| " \"corpid\": \"xxxcorpid\", \n" + | |||||
| " \"corp_name\": \"xxxx有限公司\", \n" + | |||||
| " \"corp_type\": \"unverified\", \n" + | |||||
| " \"corp_round_logo_url\": \"http://p.qpic.cn/pic_wework/3777001839/4046834be7a5f2711feaaa3cc4e691e1bcb1e526cb4544b5/0\", \n" + | |||||
| " \"corp_square_logo_url\": \"https://p.qlogo.cn/bizmail/EsvsszIt9hJrjrx8QKXuIs0iczdnV4icaPibLIViaukn1iazCay8L1UXtibA/0\", \n" + | |||||
| " \"corp_user_max\": 200, \n" + | |||||
| " \"corp_agent_max\": 300, \n" + | |||||
| " \"corp_wxqrcode\": \"http://p.qpic.cn/pic_wework/211781738/a9af41a60af7519775dd7ac846a4942979dc4a14b8bb2c72/0\", \n" + | |||||
| " \"corp_full_name\": \"xxxx有限公司\", \n" + | |||||
| " \"subject_type\": 1, \n" + | |||||
| " \"corp_scale\": \"1-50人\", \n" + | |||||
| " \"corp_industry\": \"生活服务\", \n" + | |||||
| " \"corp_sub_industry\": \"租赁和商务服务\", \n" + | |||||
| " \"location\": \"北京市\"\n" + | |||||
| " }, \n" + | |||||
| " \"auth_info\": {\n" + | |||||
| " \"agent\": [\n" + | |||||
| " {\n" + | |||||
| " \"agentid\": 1000012, \n" + | |||||
| " \"name\": \"xxxxx\", \n" + | |||||
| " \"square_logo_url\": \"http://wx.qlogo.cn/mmhead/Q3auHgzwzM4ZCtdxicN8ghMOtTv7M7rLPKmeZ3amic00btdwbNmicaW3Q/0\", \n" + | |||||
| " \"privilege\": {\n" + | |||||
| " \"level\": 1, \n" + | |||||
| " \"allow_party\": [ ], \n" + | |||||
| " \"allow_user\": [\n" + | |||||
| " \"yuanqixun\"\n" + | |||||
| " ], \n" + | |||||
| " \"allow_tag\": [ ], \n" + | |||||
| " \"extra_party\": [ ], \n" + | |||||
| " \"extra_user\": [ ], \n" + | |||||
| " \"extra_tag\": [ ]\n" + | |||||
| " }\n" + | |||||
| " }\n" + | |||||
| " ]\n" + | |||||
| " }, \n" + | |||||
| " \"auth_user_info\": {\n" + | |||||
| " \"userid\": \"yuanqixun\", \n" + | |||||
| " \"name\": \"袁启勋\", \n" + | |||||
| " \"avatar\": \"http://wework.qpic.cn/bizmail/ZYqy8EswiaFyPnk7gy7eiafoicz3TL35f4bAvCf2eSe6RVYSK7aPDFxcw/0\"\n" + | |||||
| " }\n" + | |||||
| "}"; | |||||
| final WxCpTpConfigStorage configStorage = new WxCpTpDefaultConfigImpl(); | |||||
| tpService.setWxCpTpConfigStorage(configStorage); | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| String authCode = ""; | |||||
| jsonObject.addProperty("auth_code", authCode); | |||||
| doReturn(returnJson).when(tpService).post(configStorage.getApiUrl(GET_PERMANENT_CODE), jsonObject.toString()); | |||||
| final WxCpTpPermanentCodeInfo tpPermanentCodeInfo = tpService.getPermanentCodeInfo(authCode); | |||||
| assertThat(tpPermanentCodeInfo.getAuthInfo().getAgent().get(0).getAgentid()).isEqualTo(1000012); | |||||
| assertNotNull(tpPermanentCodeInfo.getAuthInfo().getAgent().get(0).getSquareLogoUrl()); | |||||
| assertNotNull(tpPermanentCodeInfo.getAuthCorpInfo().getCorpSquareLogoUrl()); | |||||
| } | } | ||||
| @Test | @Test | ||||