功能描述:当一个用户点击一个公众号的一个特定链接,判断用户是否关注公众号,若没有关注就跳转到关注界面,已关注就跳转到指定界面。
涉及知识:
①微信网页授权
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
②获取用户基本信息
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839
微信网页授权获取code,再换取openId
1、引导用户进入授权页面同意授权,获取code
2、通过code换取网页授权access_token(与基础支持中的access_token不同)
3、如果需要,开发者可以刷新网页授权access_token,避免过期
4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
执行步骤步骤:
⑴点击特定的链接http://watermellon.vicp.io/EnterPageServlet
⑵在EnterPageServlet.java中处理,重定向到微信授权
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect,REDIRECT_URI改成自己的回调地址,我这里是http://watermellon.vicp.io/JudgeServlet
⑶在JudgeServlet.java中获取code,再通过处理得到openid
⑷最后通过获取微信用户基本信息接口得到subscribe参数(用户是否订阅该公众号标识)
accessToken contains: {
"access_token": "13_M0WCm_G3osIE........",
"expires_in": 7200,
"refresh_token": "1segRO7wOKjM........",
"openid": "o8XtO................",
"scope": "snsapi_userinfo"
}
参数说明:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839
JSON字符串: {
"subscribe": 1,
"openid": "o8X........",
"nickname": "HUJC",
"sex": 1,
"language": "zh_CN",
"city": ".....",
"province": ".......",
"country": "中国",
"headimgurl": "http://thirdwx.qlogo.cn/mmopen/hcIt14l7CHV8NnEOptFHM2HHNtLkwJHe7JuSgiavbV1rQiak7p9fb7MXJDPCIcyibFqtQoyLXHpLHMgmUjXWCXuy8Pj8y2fKw7c/132",
"subscribe_time": 1535........,
"remark": "",
"groupid": 0,
"tagid_list": [],
"subscribe_scene": "ADD_SCEN.......",
"qr_scene": 0,
"qr_scene_str": ""
}
核心代码:
EnterPageServlet.java
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8");
//这里要将你的授权回调地址处理一下,否则微信识别不了
String redirect_uri=URLEncoder.encode(Constanst.REDIRECT_URI, "UTF-8");//UTF-8编码
//简单获取openid的话参数response_type与scope与state参数固定写死即可
StringBuffer url=new StringBuffer("https://open.weixin.qq.com/connect/oauth2/authorize?redirect_uri="
+redirect_uri+"&appid="
+Constanst.APPID+"&response_type=code&scope="
+Constanst.SCOPE+"&state=STATE#wechat_redirect");
response.sendRedirect(url.toString());//这里请不要使用get请求单纯的将页面跳转到该url即可
}
JudgeServlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
String code = request.getParameter("code");
System.out.println("得到的code:"+code);
Map<String,String> params = new HashMap<String,String>();
params.put("secret", Constanst.APPSECRET);
params.put("appid", Constanst.APPID);
params.put("grant_type", "authorization_code");
params.put("code", code);
String result = HttpClientUtil.doPost(
"https://api.weixin.qq.com/sns/oauth2/access_token", params);
JSONObject jsonObject = JSONObject.fromObject(result);
System.out.println("accessToken contains:"+jsonObject);
String access_token = jsonObject.get("access_token").toString();
/*注意这里的access_token首先请注意,这里通过code换取的是一个特殊的网页授权access_token,
与基础支持中的access_token(该access_token用于调用其他接口)不同*/
String openid = jsonObject.get("openid").toString();
System.out.println("返回的openID:"+openid);
//若要获得判断用户是否关注 需要用调用接口凭证access_token 获得tokenID 全局的还有问题
if(JudgeUserUtil.judgeIsFollow("你得基础Access_token",openid)== true){
System.out.println("已关注");
}else{
System.out.println("未关注");//重定向到关注界面 或者二维码关注
}
}
HttpClientUtil.java(网上百度的工具类资源)
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8");
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
}
说明:
Constanst是一直存放常量的接口,自己设置
pom.xml中导入工具类需要的依赖
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.2.3</version>
<classifier>jdk15</classifier><!-- 指定jdk版本 -->
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.2</version>
</dependency>
创建工程接入微信公众号参考:
初探微信公众号开发-接入指南
https://www.jianshu.com/p/dc7e354be53f
特别注意:这里用的是公众号的测试号,只有测试号和认证的公众号才能用高级接口。
测试号登录地址:
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
强调:微信号只有关注了测试号才能进入服务器的网页链接,从而回调,若没有关注测试号就访问链接就会报10006未关注测试号错误,这点很坑。但是认证了的公众号在未关注的情况下是可以正常进入网页授权链接的。