微信公众号(二) --- 授权获取用户信息

                                     授权获取用户信息

1.设置 网页授权获取用户信息

2.获取授权接口 及回调接口代码

package com.hxl.controller;

import com.hxl.utils.wx.SignUtil;
import com.hxl.utils.wx.serviceUtils.OAuthServiceUtils;
import com.hxl.utils.wx.vo.AccessTokenOAuth;
import com.hxl.utils.wx.vo.UserWeiXin;
import io.swagger.annotations.Api;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

/**
 * Created by hxl on 2019/9/5.
 */
@Controller
@RequestMapping("wxService")
public class WeixinController {


    /**
     * 开启服务
     * @param request
     * @param response
     */
    @RequestMapping(method = RequestMethod.GET)
    public static void get(HttpServletRequest request, HttpServletResponse response) {
        // 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
        String signature = request.getParameter("signature");
        // 时间戳
        String timestamp = request.getParameter("timestamp");
        // 随机数
        String nonce = request.getParameter("nonce");
        // 随机字符串
        String echostr = request.getParameter("echostr");

        PrintWriter out = null;
        try {
            out = response.getWriter();
            // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,否则接入失败
            if (SignUtil.checkSignature(signature, timestamp, nonce)) {
                out.print(echostr);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            out.close();
            out = null;
        }
    }

    /**
     * 用户事件处理
     * @param request
     * @param response
     */
    @RequestMapping(method = RequestMethod.POST)
    public void post(HttpServletRequest request, HttpServletResponse response) {
        try {
            request.setCharacterEncoding("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        response.setCharacterEncoding("UTF-8");

        // 调用核心业务类接收消息、处理消息
        //String respMessage = coreService.processRequest(request);

        // 响应消息
        PrintWriter out = null;
        try {
            out = response.getWriter();
            out.print("");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            out.close();
            out = null;
        }
    }


    /**
     * 授权验证
     * @param request
     * @param response
     */
    @RequestMapping("authorize")
    public void authorize(HttpServletRequest request,HttpServletResponse response) {
        String redirectUrl = "https://域名/web/wxService/getUserWeiXin";
        //snsapi_base  snsapi_userinfo
        String url =  OAuthServiceUtils.getOauthUrl(redirectUrl,"UTF-8","snsapi_userinfo");
        System.out.println(url);
        String html = "<script type='text/javascript'>location.href='"+url+"';</script>";
        try {
            response.getWriter().print(html);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 回调  授权
     * @param code  授权code
     * @param state 参数回调
     */
    @RequestMapping(value = "/getUserWeiXin")
    public String getUserWeiXin(@RequestParam(value = "code") String code, @RequestParam(value = "state", required = false) String state) {
        //1.回调 code 和 state
        System.out.println("code=" + code + ", state=" + state);
        AccessTokenOAuth authToken = OAuthServiceUtils.getOAuthAccessToken(code);
        //2.获取 access_token 和 openid
        String access_token = authToken.getAccessToken();
        String openid = authToken.getOpenid();
        System.out.println("access_token=" + access_token+ ", openid=" + openid);
        //3.拉取用户信息
        UserWeiXin userWeiXin =  OAuthServiceUtils.getUserInfoOauth(access_token,openid);

        return "user";
    }
}

3.OAuthServiceUtils  工具类 (验证授权和用户信息接口)

package com.hxl.utils.wx.serviceUtils;

import com.hxl.utils.wx.Constant.ConstantWeChat;
import com.hxl.utils.wx.StringUtil;
import com.hxl.utils.wx.WeixinUtil;
import com.hxl.utils.wx.vo.AccessTokenOAuth;
import com.hxl.utils.wx.vo.UserWeiXin;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;

import java.net.URLEncoder;

/**
 *  授权 网页授权 和用户信息获取
 * Created by hxl on 2019/9/5.
 */
public class OAuthServiceUtils {

    public static Logger log = Logger.getLogger(OAuthServiceUtils.class);


    /**
     * wechat oauth url
     */
    public static String OAUTH = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";

    /**
     * 通过oauth获取用户详细信息
     */
    public static String GET_USER_INFO_OAUTH = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

    /**
     * 获取oauth网页认证的token
     */
    public static String GET_ACCESS_TOKEN_OAUTH = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

    /**
     * 获得Oauth认证的URL
     * @param redirectUrl	跳转的url
     * @param charset	字符集格式
     * @param scope	OAUTH scope
     * @return oauth url
     */
    public static String getOauthUrl(String redirectUrl,String charset,String scope){
        String url = "";
        try {
            url = OAUTH
                    .replace("APPID", ConstantWeChat.APPID)
                    .replace("REDIRECT_URI",
                            URLEncoder.encode(redirectUrl, charset))
                    .replace("SCOPE", scope);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return url;
    }

    /**
     *
     * 获取Access_Token(oAuth认证,此access_token与基础支持的access_token不同)
     *
     * @param code
     *            用户授权后得到的code
     * @return AccessTokenOAuth对象
     */
    public static AccessTokenOAuth getOAuthAccessToken(String code) {
        String url = GET_ACCESS_TOKEN_OAUTH
                .replace("APPID", ConstantWeChat.APPID)
                .replace("SECRET", ConstantWeChat.APPSECRET)
                .replace("CODE", code);

        JSONObject jsonObject = WeixinUtil.httpsRequest(url, "POST", null);

        AccessTokenOAuth accessTokenOAuth = null;

        if (null != jsonObject) {
            if (StringUtil.isNotEmpty(jsonObject.get("errcode"))
                    && jsonObject.get("errcode") != "0") {
                log.error("获取access_token失败 errcode:"
                        + jsonObject.getInt("errcode") + ",errmsg:"
                        + jsonObject.getString("errmsg"));
            } else {
                accessTokenOAuth = new AccessTokenOAuth();
                accessTokenOAuth.setAccessToken(jsonObject
                        .getString("access_token"));
                accessTokenOAuth.setExpiresIn(jsonObject.getInt("expires_in"));
                accessTokenOAuth.setRefreshToken(jsonObject
                        .getString("refresh_token"));
                accessTokenOAuth.setOpenid(jsonObject.getString("openid"));
                accessTokenOAuth.setScope(jsonObject.getString("scope"));
            }
        }
        return accessTokenOAuth;
    }

    /**
     * 通过oauth获取用户详细信息
     *
     * @param token
     * @param openid
     * @return UserWeiXin对象
     */
    public static UserWeiXin getUserInfoOauth(String token, String openid) {
        UserWeiXin user = null;
        if (token != null) {

            String url = GET_USER_INFO_OAUTH.replace("ACCESS_TOKEN", token)
                    .replace("OPENID", openid);

            JSONObject jsonObject = WeixinUtil.httpsRequest(url, "POST", null);

            if (null != jsonObject) {
                if (StringUtil.isNotEmpty(jsonObject.get("errcode"))
                        && jsonObject.get("errcode") != "0") {
                    log.error("获取用户信息失败 errcode:"
                            + jsonObject.getInt("errcode") + ",errmsg:"
                            + jsonObject.getString("errmsg"));
                } else {
                    user = new UserWeiXin();
                    user.setOpenid(jsonObject.getString("openid"));
                    user.setNickname(jsonObject.getString("nickname"));
                    user.setHeadimgurl(jsonObject.getString("headimgurl"));
                    user.setSex(jsonObject.getInt("sex"));
                    user.setCity(jsonObject.getString("city"));
                    user.setCountry(jsonObject.getString("country"));
                    user.setProvince(jsonObject.getString("province"));
                    user.setLanguage(jsonObject.getString("language"));
                    user.setPrivilege(jsonObject.getString("privilege"));
                }
            }
        }
        return user;
    }
}

4. wechat.properties 配置信息 及  ConstantWeChat

#测试号
token=***


appId=***
appSecret=***

encodingAESKey=***

 

public class ConstantWeChat {

    /**
     * 与接口配置信息中的Token要一致
     */
    public static String TOKEN = ConfigUtil.get("token");

    /**
     * 第三方用户唯一凭证
     */
    public static String APPID = ConfigUtil.get("appId");

    /**
     * 第三方用户唯一凭证密钥
     */
    public static String APPSECRET = ConfigUtil.get("appSecret");
}

读取配置参考: https://blog.youkuaiyun.com/qq_37184877/article/details/100656365

5.WeixinUtil 工具( https 请求) MyX509TrustManager (ssl 证书)

package com.hxl.utils.wx;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;


import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import net.sf.json.JSONObject;
import org.apache.log4j.Logger;

/**
 * Created by hxl on 2019/9/5.
 */
public class WeixinUtil {

    public static Logger log = Logger.getLogger(WeixinUtil.class);


    /**
     * 发起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;
        StringBuffer buffer = new StringBuffer();
        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 httpUrlConn = (HttpsURLConnection) url
                    .openConnection();
            httpUrlConn.setSSLSocketFactory(ssf);

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

            if ("GET".equalsIgnoreCase(requestMethod)) {
                httpUrlConn.connect();
            }

            // 当有数据需要提交时
            if (null != outputStr) {
                OutputStream outputStream = httpUrlConn.getOutputStream();
                // 注意编码格式,防止中文乱码
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 将返回的输入流转换成字符串
            InputStream inputStream = httpUrlConn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(
                    inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(
                    inputStreamReader);

            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 释放资源
            inputStream.close();
            inputStream = null;
            httpUrlConn.disconnect();
            jsonObject = JSONObject.fromObject(buffer.toString());
        } catch (ConnectException ce) {
            log.error("server connection timed out.");
        } catch (Exception e) {
            log.error("https request error:", e);
        }
        return jsonObject;
    }
}
package com.hxl.utils.wx;

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

/**
 * 证书信任管理器(用于https请求)
 * Created by hxl on 2019/9/5.
 */
public class MyX509TrustManager implements X509TrustManager {

    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}

6.示例对象 (授权对象 用户信息对象)

package com.hxl.utils.wx.vo;

import lombok.Data;

/**
 *
 * 网页授权接口调用凭证 OAuth2.0
 * Created by hxl on 2019/9/5.
 */
@Data
public class AccessTokenOAuth {

    /**
     * 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
     */
    private String accessToken;

    /**
     * access_token接口调用凭证超时时间,单位(秒)
     */
    private int expiresIn;

    /**
     * 用户刷新access_token
     */
    private String refreshToken;

    /**
     * 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
     */
    private String openid;

    /**
     * 用户授权的作用域,使用逗号(,)分隔
     */
    private String scope;

}
package com.hxl.utils.wx.vo;

import lombok.Data;

import java.util.Date;

/**
 * Created by hxl on 2019/9/5.
 */
@Data
public class UserWeiXin {

    /**
     * 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。
     */
    private Integer subscribe;

    /**
     * 用户的标识,对当前公众号唯一
     */
    private String openid;

    /**
     * 用户的昵称
     */
    private String nickname;

    /**
     * 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
     */
    private Integer sex;

    /**
     * 用户所在城市
     */
    private String city;

    /**
     * 用户所在国家
     */
    private String country;

    /**
     * 用户所在省份
     */
    private String province;

    /**
     * 用户的语言,简体中文为zh_CN
     */
    private String language;

    /**
     * 用户头像,最后一个数值代表正方形头像大小
     * (有0、46、64、96、132数值可选,0代表640*640正方形头像),
     * 用户没有头像时该项为空
     */
    private String headimgurl;

    /**
     * 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
     */
    private Date subscribe_time;

    /**
     * 微信特权
     * @return
     */
    private String privilege;
}

 7.调用文档说明

  文档说明:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

  获取详细用户信息需要非静默授权。

  找到 “ 网页授权”接口参考

  找到 “ 获取用户基本信息(包括UnionID机制)”接口参考

微信公众号获取用户信息(网页授权获取)的实现步骤如下: 1. **配置公众号**: - 登录微信公众号平台,进入“开发->“基本配置”,记录开发者ID(AppID)和开发者密钥(AppSecret)。 - 进入“公众号设置”->“功能设置”,设置“网页授权域名”,确保域名已备案并可访问。 2. **引导用户同意授权**: - 在网页中生成一个授权链接,引导用户点击该链接进行授权授权链接的格式如下: ``` https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect ``` - `APPID`:公众号的唯一标识。 - `REDIRECT_URI`:授权后重定向的回调链接地址,需进行URL编码。 - `response_type`:返回类型,请填写code。 - `scope`:应用授权作用域,`snsapi_base`(不弹出授权页面,直接跳转,只能获取用户openid)或`snsapi_userinfo`(弹出授权页面,可通过openid拿到昵称、性别、所在地等)。 - `state`:重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节。 - `#wechat_redirect`:无论直接打开还是做页面302重定向时候,必须带此参数。 3. **用户同意授权后回调**: - 用户同意授权后,微信会重定向到`REDIRECT_URI`,并在URL中携带`code`和`state`参数。 - 在回调页面中,通过`code`参数获取`access_token`和`openid`。 4. **通过code获取access_token**: - 构造请求URL: ``` https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code ``` - `APPID`:公众号的唯一标识。 - `SECRET`:公众号开发者密码。 - `CODE`:填写第一步获取的code参数。 - `grant_type`:填写authorization_code。 - 发送HTTP GET请求,获取`access_token`和`openid`。 5. **获取用户信息**: - 构造请求URL: ``` https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN ``` - `ACCESS_TOKEN`:网页授权接口调用凭证。 - `OPENID`:用户的唯一标识。 - `lang`:返回国家地区语言版本,zh_CN简体,zh_TW繁体,en英文。 - 发送HTTP GET请求,获取用户信息。 以下是一个简单的示例代码: ```javascript function getUserInfo() { // 假设用户在微信中点击了授权链接并重定向回来 const code = getQueryParam('code'); const state = getQueryParam('state'); if (code) { // 通过code获取access_token fetch(`https://api.weixin.qq.com/sns/oauth2/access_token?appid=YOUR_APPID&secret=YOUR_SECRET&code=${code}&grant_type=authorization_code`) .then(response => response.json()) .then(data => { const accessToken = data.access_token; const openid = data.openid; // 通过access_token和openid获取用户信息 fetch(`https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openid}&lang=zh_CN`) .then(response => response.json()) .then(userInfo => { console.log(userInfo); // 处理用户信息 }); }); } else { // 跳转到微信授权页面 const redirectUri = encodeURIComponent('YOUR_REDIRECT_URI'); window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=YOUR_APPID&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`; } } function getQueryParam(param) { const urlSearchParams = new URLSearchParams(window.location.search); return urlSearchParams.get(param); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值