| @@ -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"; | |||
| } | |||
| @@ -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 String getOauth2redirectUrl(); | |||
| public String getHttp_proxy_host(); | |||
| public int getHttp_proxy_port(); | |||
| @@ -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; | |||
| } | |||
| @@ -329,6 +329,54 @@ public interface WxMpService { | |||
| */ | |||
| 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请求 | |||
| * @param url | |||
| @@ -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<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 { | |||
| 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); | |||
| } | |||
| } | |||