| @@ -128,4 +128,12 @@ public class WxConsts { | |||||
| /** 弹出地理位置选择器 */ | /** 弹出地理位置选择器 */ | ||||
| public static final String LOCATION_SELECT = "location_select"; | 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"; | |||||
| } | } | ||||
| @@ -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(); | |||||
| } | |||||
| } | |||||
| @@ -25,6 +25,8 @@ public interface WxMpConfigStorage { | |||||
| public int getExpiresIn(); | public int getExpiresIn(); | ||||
| public String getOauth2redirectUrl(); | |||||
| public String getHttp_proxy_host(); | public String getHttp_proxy_host(); | ||||
| public int getHttp_proxy_port(); | public int getHttp_proxy_port(); | ||||
| @@ -20,6 +20,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| protected int http_proxy_port; | protected int http_proxy_port; | ||||
| protected String http_proxy_username; | protected String http_proxy_username; | ||||
| protected String http_proxy_password; | protected String http_proxy_password; | ||||
| protected String oauth2redirectUrl; | |||||
| public void updateAccessToken(WxAccessToken accessToken) { | public void updateAccessToken(WxAccessToken accessToken) { | ||||
| updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | ||||
| @@ -78,6 +79,15 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| this.expiresIn = expiresIn; | this.expiresIn = expiresIn; | ||||
| } | } | ||||
| @Override | |||||
| public String getOauth2redirectUrl() { | |||||
| return this.oauth2redirectUrl; | |||||
| } | |||||
| public void setOauth2redirectUrl(String oauth2redirectUrl) { | |||||
| this.oauth2redirectUrl = oauth2redirectUrl; | |||||
| } | |||||
| public String getHttp_proxy_host() { | public String getHttp_proxy_host() { | ||||
| return http_proxy_host; | return http_proxy_host; | ||||
| } | } | ||||
| @@ -329,6 +329,54 @@ public interface WxMpService { | |||||
| */ | */ | ||||
| WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; | WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; | ||||
| /** | |||||
| * <pre> | |||||
| * 构造oauth2授权的url连接 | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息 | |||||
| * </pre> | |||||
| * @param scope | |||||
| * @param state | |||||
| * @return code | |||||
| */ | |||||
| public String oauth2buildAuthorizationUrl(String scope, String state); | |||||
| /** | |||||
| * <pre> | |||||
| * 用code换取oauth2的access token | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息 | |||||
| * </pre> | |||||
| * @param code | |||||
| * @return | |||||
| */ | |||||
| public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 刷新oauth2的access token | |||||
| * </pre> | |||||
| * @param refreshToken | |||||
| * @return | |||||
| */ | |||||
| public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 用oauth2获取用户信息, 当前面引导授权时的scope是snsapi_userinfo的时候才可以 | |||||
| * </pre> | |||||
| * @param oAuth2AccessToken | |||||
| * @param lang zh_CN, zh_TW, en | |||||
| */ | |||||
| public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 验证oauth2的access token是否有效 | |||||
| * </pre> | |||||
| * @param oAuth2AccessToken | |||||
| * @return | |||||
| */ | |||||
| public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken); | |||||
| /** | /** | ||||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | ||||
| * @param url | * @param url | ||||
| @@ -27,11 +27,13 @@ import org.apache.http.client.CredentialsProvider; | |||||
| import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
| import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
| import org.apache.http.client.methods.HttpGet; | 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.BasicCredentialsProvider; | ||||
| import org.apache.http.impl.client.BasicResponseHandler; | import org.apache.http.impl.client.BasicResponseHandler; | ||||
| import org.apache.http.impl.client.CloseableHttpClient; | import org.apache.http.impl.client.CloseableHttpClient; | ||||
| import org.apache.http.impl.client.HttpClients; | import org.apache.http.impl.client.HttpClients; | ||||
| import javax.print.DocFlavor; | |||||
| import java.io.File; | import java.io.File; | ||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| @@ -297,6 +299,98 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| return WxMpSemanticQueryResult.fromJson(responseContent); | 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<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| return WxMpOAuth2AccessToken.fromJson(responseText); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException { | |||||
| String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?"; | |||||
| url += "appid=" + wxMpConfigStorage.getAppId(); | |||||
| url += "&grant_type=refresh_token"; | |||||
| url += "&refresh_token=" + refreshToken; | |||||
| try { | |||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| return WxMpOAuth2AccessToken.fromJson(responseText); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException { | |||||
| String url = "https://api.weixin.qq.com/sns/userinfo?"; | |||||
| url += "access_token=" + oAuth2AccessToken.getAccessToken(); | |||||
| url += "&openid=" + oAuth2AccessToken.getOpenId(); | |||||
| if (lang == null) { | |||||
| url += "&lang=zh_CN"; | |||||
| } else { | |||||
| url += "&lang=" + lang; | |||||
| } | |||||
| try { | |||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| return WxMpUser.fromJson(responseText); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken) { | |||||
| String url = "https://api.weixin.qq.com/sns/auth?"; | |||||
| url += "access_token=" + oAuth2AccessToken; | |||||
| url += "&openid=" + oAuth2AccessToken.getOpenId(); | |||||
| try { | |||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (WxErrorException e) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| public String get(String url, String queryParam) throws WxErrorException { | public String get(String url, String queryParam) throws WxErrorException { | ||||
| return execute(new SimpleGetRequestExecutor(), url, queryParam); | return execute(new SimpleGetRequestExecutor(), url, queryParam); | ||||
| } | } | ||||
| @@ -0,0 +1,64 @@ | |||||
| package me.chanjar.weixin.mp.bean.result; | |||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
| /** | |||||
| * Created by qianjia on 14/11/26. | |||||
| */ | |||||
| public class WxMpOAuth2AccessToken { | |||||
| private String accessToken; | |||||
| private int expiresIn = -1; | |||||
| private String refreshToken; | |||||
| private String openId; | |||||
| private String scope; | |||||
| public String getRefreshToken() { | |||||
| return refreshToken; | |||||
| } | |||||
| public void setRefreshToken(String refreshToken) { | |||||
| this.refreshToken = refreshToken; | |||||
| } | |||||
| public String getOpenId() { | |||||
| return openId; | |||||
| } | |||||
| public void setOpenId(String openId) { | |||||
| this.openId = openId; | |||||
| } | |||||
| public String getScope() { | |||||
| return scope; | |||||
| } | |||||
| public void setScope(String scope) { | |||||
| this.scope = scope; | |||||
| } | |||||
| public String getAccessToken() { | |||||
| return accessToken; | |||||
| } | |||||
| public void setAccessToken(String accessToken) { | |||||
| this.accessToken = accessToken; | |||||
| } | |||||
| public int getExpiresIn() { | |||||
| return expiresIn; | |||||
| } | |||||
| public void setExpiresIn(int expiresIn) { | |||||
| this.expiresIn = expiresIn; | |||||
| } | |||||
| public static WxMpOAuth2AccessToken fromJson(String json) { | |||||
| return WxGsonBuilder.create().fromJson(json, WxMpOAuth2AccessToken.class); | |||||
| } | |||||
| } | |||||