diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java index 8a843e94b..182602954 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java @@ -128,4 +128,12 @@ public class WxConsts { /** 弹出地理位置选择器 */ public static final String LOCATION_SELECT = "location_select"; + /////////////////////// + // oauth2网页授权的scope + /////////////////////// + /** 不弹出授权页面,直接跳转,只能获取用户openid */ + public static final String OAUTH2_SCOPE_BASE = "snsapi_base"; + /** 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 */ + public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo"; + } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/URIUtil.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/URIUtil.java new file mode 100644 index 000000000..bb671eb1c --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/URIUtil.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.common.util.http; + +import me.chanjar.weixin.common.util.StringUtils; + +import java.io.UnsupportedEncodingException; + +public class URIUtil { + + private static final String ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"; + + public static String encodeURIComponent(String input) { + if (StringUtils.isEmpty(input)) { + return input; + } + + int l = input.length(); + StringBuilder o = new StringBuilder(l * 3); + try { + for (int i = 0; i < l; i++) { + String e = input.substring(i, i + 1); + if (ALLOWED_CHARS.indexOf(e) == -1) { + byte[] b = e.getBytes("utf-8"); + o.append(getHex(b)); + continue; + } + o.append(e); + } + return o.toString(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return input; + } + + private static String getHex(byte buf[]) { + StringBuilder o = new StringBuilder(buf.length * 3); + for (int i = 0; i < buf.length; i++) { + int n = (int) buf[i] & 0xff; + o.append("%"); + if (n < 0x10) { + o.append("0"); + } + o.append(Long.toString(n, 16).toUpperCase()); + } + return o.toString(); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java index e7ee779cc..a2ef9d6a8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java @@ -25,6 +25,8 @@ public interface WxMpConfigStorage { public int getExpiresIn(); + public String getOauth2redirectUrl(); + public String getHttp_proxy_host(); public int getHttp_proxy_port(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java index d0f9c33d6..0f6dd7887 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java @@ -20,6 +20,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { protected int http_proxy_port; protected String http_proxy_username; protected String http_proxy_password; + protected String oauth2redirectUrl; public void updateAccessToken(WxAccessToken accessToken) { updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); @@ -78,6 +79,15 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { this.expiresIn = expiresIn; } + @Override + public String getOauth2redirectUrl() { + return this.oauth2redirectUrl; + } + + public void setOauth2redirectUrl(String oauth2redirectUrl) { + this.oauth2redirectUrl = oauth2redirectUrl; + } + public String getHttp_proxy_host() { return http_proxy_host; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index daaa062fa..8fc70e8e3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -329,6 +329,54 @@ public interface WxMpService { */ WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; + /** + *
+ * 构造oauth2授权的url连接 + * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息 + *+ * @param scope + * @param state + * @return code + */ + public String oauth2buildAuthorizationUrl(String scope, String state); + + /** + *
+ * 用code换取oauth2的access token + * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息 + *+ * @param code + * @return + */ + public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException; + + /** + *
+ * 刷新oauth2的access token + *+ * @param refreshToken + * @return + */ + public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException; + + /** + *
+ * 用oauth2获取用户信息, 当前面引导授权时的scope是snsapi_userinfo的时候才可以 + *+ * @param oAuth2AccessToken + * @param lang zh_CN, zh_TW, en + */ + public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException; + + /** + *
+ * 验证oauth2的access token是否有效 + *+ * @param oAuth2AccessToken + * @return + */ + public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken); + /** * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 * @param url diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index e55f261f1..5e07b7c2f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -27,11 +27,13 @@ import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.utils.URIUtils; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import javax.print.DocFlavor; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -297,6 +299,98 @@ public class WxMpServiceImpl implements WxMpService { return WxMpSemanticQueryResult.fromJson(responseContent); } + @Override + public String oauth2buildAuthorizationUrl(String scope, String state) { + String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" ; + url += "appid=" + wxMpConfigStorage.getAppId(); + url += "&redirect_uri=" + URIUtil.encodeURIComponent(wxMpConfigStorage.getOauth2redirectUrl()); + url += "&response_type=code"; + url += "&scope=" + scope; + if (state != null) { + url += "&state=" + state; + } + url += "#wechat_redirect"; + return url; + } + + @Override + public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException { + String url = "https://api.weixin.qq.com/sns/oauth2/access_token?"; + url += "appid=" + wxMpConfigStorage.getAppId(); + url += "&secret=" + wxMpConfigStorage.getSecret(); + url += "&code=" + code; + url += "&grant_type=authorization_code"; + + try { + RequestExecutor