首先要知道的几点:
1、让用户扫描我们的二维码首先需要知道扫描者是否已经关注该微信公众号。
2、我们需要知道自己微信公众号的唯一标识。
微信公众号中提供了开发者测试公众号,地址:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
其中公众号中两个主要的信息是:appid和appsecret。相当于用户名和密码。
公众号的借口访问需要使用access_token,而appid和appsecret则是访问的凭据。
微信公众平台已经提供获取access_token的访问接口:
注意是HTTPS请求:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
因为每一个用户在某个公众号的openid都是唯一的,所以需要知道某个用户是否在微信公众号中是否有关注,则需要让微信平台知道需要关注的公众号的信息,才能提供扫描用户在公众号的openid。所以公众号平台提供了此接口,为在未关注或已关注状态的用户网页访问公众号时,提供用户在公众号的唯一openid。
使用的访问链接:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
注意:参数说明如下:
如果知道了用户所在公众号的唯一openid,则可以根据其调用平台的另一个获取用户基本信息的接口获取关注状态。
接口地址:GET https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
返回值以json格式展示:如下是接口参数说明,可以使用subscribe参数判断是否已经关注:0-未关注,1-已关注。
由于微信已经屏蔽掉,网页中通过点击按钮弹出关注页面的方式。可以使用以下链接,访问公众号的页面,可以直接点击图标进行关注。
https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz="+biz+"&chksm==&scene=110#wechat_redirect
注意:biz的值不是appid,而是一种特殊字符串。获取方式是:1、分享公众号的一篇文章到其他应用(除了微信)。2、复制链接,可以看到biz参数的值。
以上是需要考虑到的基本信息,接下来就是实现逻辑。
1、业务中提供需要访问链接,以二维码的方式访问。
2、二维码中跳转的链接是:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
3、DEDIRECT_URI路径是我们处理判断逻辑的后台controller。注意:如果是公众号域名需要通过授权,在公众号接口参数中设置。
4、第2点的接口中会传递code参数给处理类。处理类中接收code参数,通过code参数获取access_token和openid参数。
5、注意此access_token参数有效期很短,如有需要可以刷新access_token,或再重新获取。
6、根据openid和access_token获取用户在公众号中的基本信息。
7、如果返回的subscribe的值为1,则表示已经关注,直接跳转业务的其他页面。如果为0,则表示未关注,直接根据https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz="+biz+"&chksm==&scene=110#wechat_redirect跳转到公众号的详细页。
实现代码:
判断跳转的处理类JudgeWeiXinServlet.java
package com.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.exception.ExceptionUtils;
import net.sf.json.JSONObject;
import com.entity.Entity;
import com.util.HttpClientUtil;
public class JudgeWeiXinServlet extends HttpServlet {
public static final String APPID = "wx1083c8787cd2b981";//叮叮当当公众号开发者id
public static final String BIZ = "MzI1MTY5MTM2OA%3D%3D&chksm";//跳转到公众号的识别码
public static final String SECRET = "db515d9f3f90a3e4cc9a7759aeb7114d";//叮叮当当公众号开发者密码
public static final String CHARSET = "utf-8";
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request,response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String code = request.getParameter("code");
String id = request.getParameter("id");
try{
// out.print(isUserSubscribe(code,id,out)?"<br/><h1>has subscribe</h1>":"<br/><h1>has not subscribe</h1>");
if(isUserSubscribe(code,id,out)){
//如果已经关注,跳转到项目明细中
response.sendRedirect("http://www.ddscan.cn/commoditysaom.htm?coid=133");
}
else{
//未关注,跳转到叮叮当当公众号首页,供关注
response.sendRedirect("https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz="+BIZ+"==&scene=110#wechat_redirect");
// out.print("<a href=\"weixin://contacts/profile/RossLv000\"><b>Contacts Me</b></a>");
}
}
catch(Exception ex){
out.print("exception:"+ex.getMessage());
}
out.flush();
out.close();
}
/** 判断扫描着是否关注公众号
* @param code
* @param id
* @return true-是,false-否
*/
private static boolean isUserSubscribe(String code,String id,PrintWriter out)throws Exception{
boolean res = false;
//根据用户的openid和公众号开发者id查询用户在公众号是否有关注
try{
//根据code获取openid和Access_token
//https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+APPID+"&secret="+SECRET+"&code="+code+"&grant_type=authorization_code";
String result = HttpClientUtil.doGetSSL(url, CHARSET);
System.out.println("1============:"+result);
out.print("1===========:"+result+"<br/>");
if(null==result||result.equals("")){
return false;
}
// json字符串转JavaBean
JSONObject jsonObject = JSONObject.fromObject(result);
Map resultMap = (HashMap) JSONObject.toBean(jsonObject, HashMap.class);
Object errmsg = resultMap.get("errmsg");
if(null!=errmsg){
throw new Exception(errmsg.toString());
}
String access_token = resultMap.get("access_token").toString();
String openid = resultMap.get("openid").toString();
System.out.println("获取的access_token:"+access_token);
System.out.println("openid="+openid);
//2、重新获取access_token
//https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+APPID+"&secret="+SECRET;
result = HttpClientUtil.doGetSSL(url, CHARSET);
System.out.println("2============:"+result);
out.print("2===========:"+result+"<br/>");
if(null==result||result.equals("")){
return false;
}
// json字符串转JavaBean
jsonObject = JSONObject.fromObject(result);
resultMap = (HashMap) JSONObject.toBean(jsonObject, HashMap.class);
errmsg = resultMap.get("errmsg");
if(null!=errmsg){
throw new Exception(errmsg.toString());
}
access_token = resultMap.get("access_token").toString();
//3、根据access_token和openid获取用户信息
url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+access_token+"&openid="+openid+"&lang=zh_CN";
result = HttpClientUtil.doGetSSL(url, CHARSET);
if(null==result||result.equals("")){
return false;
}
System.out.println("3===========:"+result+"<br/>");
out.print("3===========:"+result);
jsonObject = JSONObject.fromObject(result);
Entity entity = (Entity) JSONObject.toBean(jsonObject, Entity.class);
int subscribe = entity.getSubscribe();//获取用户描述符,0-未关注,1-已关注
res = (subscribe==1);//如果为1则已经关注
}
catch(Exception ex){
throw new Exception(ExceptionUtils.getFullStackTrace(ex));
}
return res;
}
public static void main(String[] args) {
String a = "{\"subscribe\":1,\"openid\":\"aa\",\"nickname\":\"Ross\",\"tagid_list\":[]}";
JSONObject jsonObject = JSONObject.fromObject(a);
Entity result = (Entity) JSONObject.toBean(jsonObject, Entity.class);
System.out.println(result.getSubscribe());
}
}
为可以模拟浏览器访问https协议,使用HttpClient工具类,并封装成HttpClientUtil类和忽略https验证的SSLClient类。
HttpClientUtil类:
package com.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import net.sf.json.JSONObject;
public class HttpClientUtil {
/**
* https方式 做doget请求
* @param url 请求路径
* @param charset 返回编码,默认是utf-8
* @return
*/
public static String doGetSSL(String url,String charset){
if(null == charset){
charset = "utf-8";
}
HttpClient httpClient = null;
HttpGet httpGet= null;
String result = null;
try {
httpClient = new SSLClient();
httpGet = new HttpGet(url);
HttpResponse response = httpClient.execute(httpGet);
if(response != null){
HttpEntity resEntity = response.getEntity();
if(resEntity != null){
result = EntityUtils.toString(resEntity,charset);
}
}
} catch (Exception e) {
try {
throw new Exception(e);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
return result;
}
/** HTTPS方式 做dopost请求
* @param url
* @param map
* @param charset
* @return
*/
public static String doPostSSL(String url,Map<String,String> map,String charset){
HttpClient httpClient = null;
HttpPost httpPost = null;
String result = null;
try{
httpClient = new SSLClient();
httpPost = new HttpPost(url);
//����
List<NameValuePair> list = new ArrayList<NameValuePair>();
Iterator iterator = map.entrySet().iterator();
while(iterator.hasNext()){
Entry<String,String> elem = (Entry<String, String>) iterator.next();
list.add(new BasicNameValuePair(elem.getKey(),elem.getValue()));
}
if(list.size() > 0){
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list,charset);
httpPost.setEntity(entity);
}
HttpResponse response = httpClient.execute(httpPost);
if(response != null){
HttpEntity resEntity = response.getEntity();
if(resEntity != null){
result = EntityUtils.toString(resEntity,charset);
}
}
}catch(Exception ex){
ex.printStackTrace();
}
return result;
}
/** HTTPS 方式 做dopost请求,格式为JSON
* @param url
* @param params
* @param charset
* @return
*/
public static String doJsonPostSSL(String url,Map params,String charset){
HttpClient httpClient = null;
HttpPost httpPost = null;
String result = null;
try{
httpClient = new SSLClient();
httpPost = new HttpPost(url);
JSONObject object = new JSONObject();
Iterator it = params.keySet().iterator();
while(it.hasNext()){
Object key = it.next();
object.put(key, params.get(key));
}
// Map sendMap = new HashMap();
// sendMap.put("scene", new Scene(123));
// JSONObject object = new JSONObject();
// object.put("expire_seconds", 1800);
// object.put("action_name", "QR_SCENE");
// object.put("action_info", sendMap);
System.out.println("json:"+object.toString());
StringEntity entity = new StringEntity(object.toString(),charset);
entity.setContentEncoding(charset);
entity.setContentType("application/json");
httpPost.setEntity(entity);
HttpResponse response = httpClient.execute(httpPost);
if(response != null){
HttpEntity resEntity = response.getEntity();
if(resEntity != null){
result = EntityUtils.toString(resEntity,charset);
}
}
}
catch(Exception ex){
ex.printStackTrace();
}
return result;
}
/** doget请求,解析返回的文件流
* @param filePath
* @param url
*/
public static void dogetContent(String filePath,String url){
HttpClient httpClient = null;
HttpGet httpGet= null;
try {
httpClient = new SSLClient();
httpGet = new HttpGet(url);
HttpResponse response = httpClient.execute(httpGet);
if(response != null){
HttpEntity resEntity = response.getEntity();
if(resEntity != null){
InputStream is = resEntity.getContent();
if(is!=null)
writeToLocal(filePath, is);
System.out.println("����ɹ�");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 将InputStream信息流写入文件
* @param destination 目标文件路径
* @param input 输入流信息
* @throws IOException
*/
private static void writeToLocal(String destination, InputStream input)
throws IOException {
int index;
byte[] bytes = new byte[1024];
FileOutputStream downloadFile = new FileOutputStream(destination);
while ((index = input.read(bytes)) != -1) {
downloadFile.write(bytes, 0, index);
downloadFile.flush();
}
downloadFile.close();
input.close();
}
}
SSLClient类:
package com.util;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
public class SSLClient extends DefaultHttpClient{
public SSLClient() throws Exception{
super();
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = this.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", 443, ssf));
}
}
为方便解析返回的json信息,编写的实体类:Entity类
package com.entity;
import java.util.List;
public class Entity {
private int subscribe;
private String openid;
private List tagid_list;
public int getSubscribe() {
return subscribe;
}
public void setSubscribe(int subscribe) {
this.subscribe = subscribe;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public List getTagid_list() {
return tagid_list;
}
public void setTagid_list(List tagid_list) {
this.tagid_list = tagid_list;
}
}