@@ -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); | |||||
} | |||||
} |