(三)微信开发获取access_token,openid

首先我们要清楚h5页面的请求流程

在第一次授权的时候

客户端请求url

微信返回客户端一个code

当用户点击同意授权的时候,将code带过去,获取access_token和openId ,code有效期为5分钟,access_token为全局的唯一票据,有效期为两个小时,当过期的时候需重新获取

openid是用户在此公众服务号的一个唯一标识

access_token是当你要去微信服务器去请求用户基本信息的时候的一个凭证

简单来说 就是   客户端————微信服务器————第三方服务器

三者之间进行交互

首先先新建一个微信数据存放类WeixinOauth2Token

package com.wonder.entity;

/**
 * Created by Guozhijie on 2016/10/27.
 */
public class WeixinOauth2Token {
    // 网页授权接口调用凭证
    private String accessToken;
    // 凭证有效时长
    private int expiresIn;
    // 用于刷新凭证
    private String refreshToken;
    // 用户标识
    private String openId;
    // 用户授权作用域
    private String 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 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;
    }
}

微信用户信息类weixinUserInfo

package com.wonder.entity;

/**
 * Created by Guozhijie on 2016/10/27.
 */
public class WeixinUserInfo {
    // 用户的标识
    private String openId;
    // 关注状态(1是关注,0是未关注),未关注时获取不到其余信息
    private int subscribe;
    // 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
    private String subscribeTime;
    // 昵称
    private String nickname;
    // 用户的性别(1是男性,2是女性,0是未知)
    private int sex;
    // 用户所在国家
    private String country;
    // 用户所在省份
    private String province;
    // 用户所在城市
    private String city;
    // 用户的语言,简体中文为zh_CN
    private String language;
    // 用户头像
    private String headImgUrl;

    public String getOpenId() {
        return openId;
    }

    public void setOpenId(String openId) {
        this.openId = openId;
    }

    public int getSubscribe() {
        return subscribe;
    }

    public void setSubscribe(int subscribe) {
        this.subscribe = subscribe;
    }

    public String getSubscribeTime() {
        return subscribeTime;
    }

    public void setSubscribeTime(String subscribeTime) {
        this.subscribeTime = subscribeTime;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public String getHeadImgUrl() {
        return headImgUrl;
    }

    public void setHeadImgUrl(String headImgUrl) {
        this.headImgUrl = headImgUrl;
    }
}

服务器证书通行证类

package com.wonder.Util;

import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Created by Guozhijie on 2016/10/27.
 */
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 com.wonder.Util;

import com.wonder.entity.WeixinOauth2Token;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.ConnectException;
import java.net.URL;

/**
 * Created by Guozhijie on 2016/10/27.
 */
public class CommonUtil {

    private static Logger log = LoggerFactory.getLogger(CommonUtil.class);

    // 凭证获取(GET)
    public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";

    /**
     * 发送https请求
     *
     * @param requestUrl 请求地址
     * @param requestMethod 请求方式(GET、POST)
     * @param outputStr 提交的数据
     * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
     */
    public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        JSONObject jsonObject = null;
        try {
            // 创建SSLContext对象,并使用我们指定的信任管理器初始化
            TrustManager[] tm = { new MyX509TrustManager() };
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式(GET/POST)
            conn.setRequestMethod(requestMethod);

            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }

            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            jsonObject = JSONObject.fromObject(buffer.toString());
        } catch (ConnectException ce) {
            log.error("连接超时:{}", ce);
        } catch (Exception e) {
            log.error("https请求异常:{}", e);
        }
        return jsonObject;
    }

    /**
     * 获取接口访问凭证
     *
     * @param appid 凭证
     * @param appsecret 密钥
     * @return
     */
    public static WeixinOauth2Token getToken(String appid, String appsecret, HttpServletRequest request) {
        WeixinOauth2Token wat = null;
        String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        requestUrl = requestUrl.replace("APPID", appid);
        requestUrl = requestUrl.replace("SECRET", appsecret);
        requestUrl = requestUrl.replace("CODE", request.getParameter("code"));
        // 发起GET请求获取凭证
        JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);

        if (null != jsonObject) {
            try {
                wat = new WeixinOauth2Token();
                wat.setAccessToken(jsonObject.getString("access_token"));
                wat.setExpiresIn(jsonObject.getInt("expires_in"));
                wat.setRefreshToken(jsonObject.getString("refresh_token"));
                wat.setOpenId(jsonObject.getString("openid"));
                wat.setScope(jsonObject.getString("scope"));
            } catch (JSONException e) {
                wat = null;
                // 获取token失败
                log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
            }
        }
        return wat;
    }

    /**
     * URL编码(utf-8)
     *
     * @param source
     * @return
     */
    public static String urlEncodeUTF8(String source) {
        String result = source;
        try {
            result = java.net.URLEncoder.encode(source, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 根据内容类型判断文件扩展名
     *
     * @param contentType 内容类型
     * @return
     */
    public static String getFileExt(String contentType) {
        String fileExt = "";
        if ("image/jpeg".equals(contentType))
            fileExt = ".jpg";
        else if ("audio/mpeg".equals(contentType))
            fileExt = ".mp3";
        else if ("audio/amr".equals(contentType))
            fileExt = ".amr";
        else if ("video/mp4".equals(contentType))
            fileExt = ".mp4";
        else if ("video/mpeg4".equals(contentType))
            fileExt = ".mp4";
        return fileExt;
    }
}

然后在访问的时候做一个拦截interceptor

package com.wonder.interceptor;

import com.wonder.Util.CommonUtil;
import com.wonder.entity.WeixinOauth2Token;
import com.wonder.entity.WeixinUserInfo;
import com.wonder.threadLocal.UserIdThreadLocal;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * Created by Guozhijie on 2016/10/27.
 */
public class WechatInterceptor  implements HandlerInterceptor {
    public   boolean preHandle(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse, Object handler) throws Exception{
        String uri=httpServletRequest.getRequestURI();
        WeixinUserInfo user=(WeixinUserInfo) httpServletRequest.getSession().getAttribute("user");
        if(user!=null){
            UserIdThreadLocal.setOpenId(Long.parseLong(user.getOpenId()));
            return true;
        }
        else{

            try{
                String code=httpServletRequest.getParameter("code");
                if(StringUtils.isEmpty(code)){
                   httpServletResponse.sendRedirect(getUrl(httpServletRequest));
                    return false;
                }
                else{
                    String appid="wxd1f88b3343475e07";
                    String appSecret="*************";
                    WeixinOauth2Token token= CommonUtil.getToken(appid,appSecret,httpServletRequest);
                    UserIdThreadLocal.setOpenId( Long.parseLong(token.getOpenId()));
                    WeixinUserInfo weixinUserInfo=new WeixinUserInfo();
                    weixinUserInfo.setOpenId(token.getOpenId());
                    httpServletRequest.getSession().setAttribute("user",weixinUserInfo);
                    return true;
                }
            }catch (Exception e){
                e.printStackTrace();
                httpServletResponse.sendRedirect(getUrl(httpServletRequest));
                return false;
            }

        }

    }

    @Override
    public   void postHandle(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse, Object o, org.springframework.web.servlet.ModelAndView modelAndView) throws Exception{}

    @Override
    public void afterCompletion(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception{}


    public String getUrl(HttpServletRequest request)throws UnsupportedEncodingException{
       String requestUrl=request.getRequestURL().toString();
        String redirectUri= URLEncoder.encode(requestUrl,"utf-8");
        String appid="wxd1f88b3343475e07";
        String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appid+"&redirect_uri="+redirectUri+"&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
        return url;
    }
}

拦截的作用就是在开始授权通过时候,如果此用户是第一次,则走login方法,然后通过appId ,跟secret ,code 来获取access_token 和openid ,unionid 并存入数据库

如果已经在session中有了此用户则直接访问

若访问没有code参数,且没有授权  则走geturl获取code 去授权页面

此时有一个疑问就是当你 授权了   且 第二次访问此url的时候是怎么一个访问情况,

其实在你第二次访问的时候 若你的Session已过期了,还可以访问,是因为在微信服务器端 根据你的unionid

来判断你是否做过授权 然后再给你一个code 此时 由于有了code参数   拿到openId去数据库已查询发现有了此用户,可以直接访问

需要注意的是 scope 权限,分两种  

应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息

猜你喜欢

转载自guozhijie87.iteye.com/blog/2334405