1:用户同意授权,获取code
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
替换URL中的APPID&REDIRECT_URI等参数
尤其注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问
当用户访问并确认授权之后将转发至REDIRECT_URI”。
2:通过code换取网页授权access_token
https://api.weixin.qq.com/sns/oauth2/access_token
/**
* 获取授权凭证token
* @param key 应用appid
* @param secret 应用密匙
* @return json格式的字符串
*/
public static String getAccessToken(String appid, String secret,String code) {
TreeMap<String, String> map = new TreeMap<>();
map.put("appid", appid);
map.put("secret", secret);
map.put("code", code);
map.put("grant_type", "authorization_code");
String json = HttpReqUtil.HttpDefaultGetExecute(WechatConfig.GET_AUTH_TOKEN_URL, map,
"");
logger.debug("WechatUtil getAuthAccessToken,result: "+json);
String result = null;
WeixinAuthToken accessToken = JsonHelper.DeserializeObject(json, WeixinAuthToken .class);
return accessToken ;
}
@Data
public class WeixinAuthToken {
String access_token;
Long expires_in;
String refresh_token;
String openid;
String scope;
String unionid;
}
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
获取openid和unionid 和网页系统的用户信息关联。
3:刷新access_token(如果需要)
https://api.weixin.qq.com/sns/oauth2/refresh_token
/**
* 获取授权凭证token
* @param key 应用appid
* @param token 通过access_token获取到的refresh_token参数
* @return json格式的字符串
*/
public static String refreshAccessToken(String appid, String token) {
TreeMap<String, String> map = new TreeMap<>();
map.put("appid", appid);
map.put("grant_type", "refresh_token");
map.put("refresh_token", token);
String json = HttpReqUtil.HttpDefaultGetExecute(WechatConfig.REFRESH_AUTH_TOKEN_URL, map,
"");
logger.debug("WechatUtil getAuthAccessToken,result: "+json);
String result = null;
WeixinAuthToken accessToken = JsonHelper.DeserializeObject(json, WeixinAuthToken .class);
return accessToken ;
}
4:拉取用户信息(需scope为 snsapi_userinfo)
https://api.weixin.qq.com/sns/userinfo
/**
* 获取授权凭证token
* @param openid 微信用户openid
* @param token 通过access_token获取到的refresh_token参数
* @return json格式的字符串
*/
public static String getWeixinSNSUser(String token,String openid) {
TreeMap<String, String> map = new TreeMap<>();
map.put("access_token", token);
map.put("openid", openid);
map.put("lang", "zh_CN");
String json = HttpReqUtil.HttpDefaultGetExecute(WechatConfig.REFRESH_AUTH_TOKEN_URL, map,
"");
logger.debug("WechatUtil getAuthAccessToken,result: "+json);
String result = null;
WeixinUser weixinUser = JsonHelper.DeserializeObject(json, WeixinUser .class);
return accessToken ;
}
@Data
public class WeixinUser {
String subscribe;
String openid;
String nickname;
String sex;
String language;
String city;
String province;
String country;
String headimgurl;
String subscribe_time;
String unionid;
String remark;
String groupid;
String[] tagid_list;
String subscribe_scene;
String qr_scene;
String qr_scene_str;
}
Http请求工具类
package weixin.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
/**
* Http连接工具类
* @author huizhe.yu
*/
public class HttpReqUtil {
private static int DEFAULT_CONNTIME = 5000;
private static int DEFAULT_READTIME = 5000;
public static final String DEFAULT_CHARACTER_ENCODING = "UTF-8";
public static final String GET_METHOD = "GET";
public static final String POST_METHOD = "POST";
/**
* http请求
* @param method 请求方法GET/POST
* @param path 请求路径
* @param timeout 连接超时时间 默认为5000
* @param readTimeout 读取超时时间 默认为5000
* @param data 数据
* @return
*/
private static String defaultConnection(String method, String path, int timeout, int readTimeout, String data)
throws Exception {
String result = "";
URL url = new URL(path);
if (url != null) {
HttpURLConnection conn = getConnection(method, url);
conn.setConnectTimeout(timeout == 0 ? DEFAULT_CONNTIME : timeout);
conn.setReadTimeout(readTimeout == 0 ? DEFAULT_READTIME : readTimeout);
if (StringUtils.isNoneBlank(data)) {
OutputStream output = conn.getOutputStream();
output.write(data.getBytes(DEFAULT_CHARACTER_ENCODING));
output.flush();
}
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
InputStream input = conn.getInputStream();
result = IOUtil.inputStreamToString(input, DEFAULT_CHARACTER_ENCODING);
conn.disconnect();
}
}
return result;
}
/**
* 根据url的协议选择对应的请求方式
* @param method 请求的方法
* @return
* @throws IOException
*/
private static HttpURLConnection getConnection(String method, URL url) throws IOException {
HttpURLConnection conn = null;
if ("https".equals(url.getProtocol())) {
SSLContext context = null;
try {
context = SSLContext.getInstance("SSL", "SunJSSE");
context.init(new KeyManager[0], new TrustManager[] { new MyX509TrustManager() },
new java.security.SecureRandom());
} catch (Exception e) {
throw new IOException(e);
}
HttpsURLConnection connHttps = (HttpsURLConnection) url.openConnection();
connHttps.setSSLSocketFactory(context.getSocketFactory());
connHttps.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
conn = connHttps;
} else {
conn = (HttpURLConnection) url.openConnection();
}
conn.setRequestMethod(method);
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
return conn;
}
/**
* 设置参数
* @param map 参数map
* @param path 需要赋值的path
* @param charset 编码格式 默认编码为utf-8(取消默认)
* @return 已经赋值好的url 只需要访问即可
*/
public static String setParmas(Map<String, String> map, String path, String charset) throws Exception {
String result = "";
boolean hasParams = false;
if (path != null && !"".equals(path)) {
if (MapUtils.isNotEmpty(map)) {
StringBuilder builder = new StringBuilder();
Set<Entry<String, String>> params = map.entrySet();
for (Entry<String, String> entry : params) {
String key = entry.getKey().trim();
String value = entry.getValue().trim();
if (hasParams) {
builder.append("&");
} else {
hasParams = true;
}
if (charset != null && !"".equals(charset)) {
// builder.append(key).append("=").append(URLDecoder.(value,charset));
builder.append(key).append("=").append(IOUtil.urlEncode(value, charset));
} else {
builder.append(key).append("=").append(value);
}
}
result = builder.toString();
}
}
return doUrlPath(path, result).toString();
}
/**
* 设置连接参数
* @param path 路径
* @return
*/
private static URL doUrlPath(String path, String query) throws Exception {
URL url = new URL(path);
if (StringUtils.isEmpty(path)) {
return url;
}
if (StringUtils.isEmpty(url.getQuery())) {
if (path.endsWith("?")) {
path += query;
} else {
path = path + "?" + query;
}
} else {
if (path.endsWith("&")) {
path += query;
} else {
path = path + "&" + query;
}
}
return new URL(path);
}
/**
* 默认的http请求执行方法,返回
* @param method 请求的方法 POST/GET
* @param path 请求path 路径
* @param map 请求参数集合
* @param data 输入的数据 允许为空
* @return
*/
public static String HttpDefaultExecute(String method, String path, Map<String, String> map, String data) {
String result = "";
try {
String url = setParmas((TreeMap<String, String>) map, path, "");
result = defaultConnection(method, url, DEFAULT_CONNTIME, DEFAULT_READTIME, data);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static String HttpDefaultPostExecute(String path, Map<String, String> map, String data) {
String result = "";
try {
String url = setParmas((TreeMap<String, String>) map, path, "");
result = defaultConnection(POST_METHOD, url, DEFAULT_CONNTIME, DEFAULT_READTIME, data);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static String HttpDefaultGetExecute(String path, Map<String, String> map, String data) {
String result = "";
try {
String url = setParmas((TreeMap<String, String>) map, path, "");
result = defaultConnection(GET_METHOD, url, DEFAULT_CONNTIME, DEFAULT_READTIME, data);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
package weixin.utils;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import common.util.JsonHelper;
import weixin.entity.WeixinTokenBase;
public class WechatUtil {
protected static Logger logger = LoggerFactory.getLogger(WechatUtil.class);
/**
* 获取授权凭证token
* @param key 应用appid
* @param secret 应用密匙
* @return json格式的字符串
*/
public static String getAccessToken(String appid, String secret) {
TreeMap<String, String> map = new TreeMap<>();
map.put("grant_type", "client_credential");
map.put("appid", appid);
map.put("secret", secret);
String json = HttpReqUtil.HttpDefaultGetExecute(WechatConfig.GET_ACCESS_TOKEN_URL, map,
"");
logger.debug("WechatUtil getAccessToken,result: "+json);
String result = null;
WeixinTokenBase accessToken = JsonHelper.DeserializeObject(json, WeixinTokenBase.class);
if (accessToken != null) {
result = accessToken.getAccess_token();
}
return result;
}
}
HTTPS 微信信任管理器:
package weixin.utils;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* @author yuhuizhe
* 信任管理器
*/
public class MyX509TrustManager implements X509TrustManager{
// 检查客户端证书
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
// 检查服务器端证书
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
// 返回受信任的X509证书数组
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
package weixin.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
/**
* IO流工具类
* huizhe.yu
*/
public class IOUtil {
/**
* description:将输入流转换为字符串
* @param is 待转换为字符串的输入流
* @return 由输入流转换String的字符串
* @throws IOException
*/
public static String inputStreamToString(InputStream inputStream, String encoding) throws IOException {
return IOUtils.toString(inputStream, encoding);
}
/**
* description: 将字符串转换为输入流
* @param sInputString 待转换为输入流的字符串
* @return
* @throws IOException
*/
public static InputStream toInputStream(String inputStr, String encoding) throws IOException {
if (StringUtils.isEmpty(inputStr)) {
return null;
}
return IOUtils.toInputStream(inputStr, encoding);
}
/**
* description:编码
* @param source
* @param encode
* @return
*/
public static String urlEncode(String source, String encode) {
String result = source;
try {
result = URLEncoder.encode(source, encode);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
/**
* description:将输入流转换字节数组
* @param in
* @return
* @throws IOException
*/
public static byte[] inputStreamToByteArray(InputStream inputStream) throws IOException {
return IOUtils.toByteArray(inputStream);
}
}