微信公众号——网页授权


本文借鉴于:

https://blog.csdn.net/weixin_45925109/article/details/108515129?spm=1001.2014.3001.5501
https://blog.csdn.net/victoyr/article/details/89648017

两篇博客,如需观看请移步。

1. 准备

微信开发平台网址:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

由于我这里是没有微信公众号的,只能使用官方提供给我们的测试账号,地址如下:
http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

这里最好是准备一台已经备案过得服务器,我这里也是尝试了很多中办法,最后无奈找朋友借了一台备案的服务器,才测试通过。


2. 测试

创建一个 spring boot 项目,勾选 web 依赖即可。创建一个 HelloController 接口代码如下:

package org.javaboy.wechat_login.controller;

import org.javaboy.wechat_login.utils.SignUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Enumeration;

/**
 * 描述:
 *
 * @Author: YLq
 * @DateTime: 2021/7/15 17:08
 * @Version 1.0
 **/
@RestController
public class HelloController {
    @GetMapping("hello")
    public String hello(){
        return "hello";
    }
    
    /**
    * 微信测试接口
    */
    @RequestMapping(value = "/captchaImage")
    public void get(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("========WechatController========= ");
        Enumeration pNames = request.getParameterNames();
        while (pNames.hasMoreElements()) {
            String name = (String) pNames.nextElement();
            String value = request.getParameter(name);
            // out.print(name + "=" + value);
            String log = "name =" + name + "     value =" + value;
        }
        System.out.println("========WechatControllertets========= ");
        String signature = request.getParameter("signature");/// 微信加密签名
        String timestamp = request.getParameter("timestamp");/// 时间戳
        String nonce = request.getParameter("nonce"); /// 随机数
        String echostr = request.getParameter("echostr"); // 随机字符串
        PrintWriter out = response.getWriter();

        System.out.println("signature:微信加密签名\t"+signature+"\n"+"timestamp:时间戳\t"+timestamp+"\n"+"nonce:随机数\t"+nonce+"\n"+"echostr:随机字符串\t"+echostr);

        if (SignUtil.checkSignature(signature, timestamp, nonce)) {
            System.out.println("校验成功!!!!!!!!!!!!!");
            out.print(echostr);
        }
        out.close();
        out = null;
    }
}

创建 SignUtil 校验的工具类:

package org.javaboy.wechat_login.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/**
 * 描述:
 *
 * @Author: YLq
 * @DateTime: 2021/7/16 15:34
 * @Version 1.0
 **/
public class SignUtil {

    // 与接口配置信息中的Token要一致
    private static String token = "mytoken";

    /**
     * 验证签名
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] arr = new String[] { token, timestamp, nonce };
        // 将token、timestamp、nonce三个参数进行字典序排序
        Arrays.sort(arr);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        MessageDigest md = null;
        String tmpStr = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        content = null;
        // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
        return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
    }
    /**
     * 将字节数组转换为十六进制字符串
     * @param byteArray
     * @return
     */
    private static String byteToStr(byte[] byteArray) {
        String strDigest = "";
        for (int i = 0; i < byteArray.length; i++) {
            strDigest += byteToHexStr(byteArray[i]);
        }
        return strDigest;
    }
    /**
     * 将字节转换为十六进制字符串
     * @param mByte
     * @return
     */
    private static String byteToHexStr(byte mByte) {
        char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];
        String s = new String(tempArr);
        return s;
    }
}

接着,在微信提供给我们的 测试号管理 中,进行测试。
在这里插入图片描述
url,解释我们后台程序的 url 地址,是带有域名的地址
token,后台配置的 token

测试结果如下:
在这里插入图片描述

证明可以访问到我们的程序了。

注意: url 地址,必须是一个备案的服务器的域名,上面的博客中提到了使用小米球(我也尝试使用了花生壳)。虽然,在这个环节上是可以发送给到自己的程序上的,但是在接下来,微信用户同意授权的步骤中,在微信上发送 url 地址会被微信屏蔽掉,所以不建议使用。


3. 网页授权

JS接口安全域名,这里直接放置自己服务器的域名。
在这里插入图片描述

测试号二维码
扫码关注该公众号,然后进行测试。
在这里插入图片描述

接着修改网页授权获取用户基本信息:
在这里插入图片描述
这里修改,填的是域名
在这里插入图片描述
接着就是代码部分,我这里按照上面博客的代码进行了少量的删减,如有不清楚的请移步上面的博客。

创建 WechatLoginController

package org.javaboy.wechat_login.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.javaboy.wechat_login.utils.WechatUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;

/**
 * 描述:
 *
 * @Author: YLq
 * @DateTime: 2021/7/16 16:06
 * @Version 1.0
 **/
@Controller
@RequestMapping("wechatlogin")
public class WechatLoginController {

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

    @RequestMapping(value = "/logincheck", method = { RequestMethod.GET })
    public String doGet(HttpServletRequest request, HttpServletResponse response) {
        log.debug("weixin login get...");
        // 获取微信公众号传输过来的code,通过code可获取access_token,进而获取用户信息
        String code = request.getParameter("code");
        // 这个state可以用来传我们自定义的信息,方便程序调用,这里也可以不用
        // String roleType = request.getParameter("state");
        log.debug("weixin login code:" + code);
        String user = null;
        String openId = null;
        if (null != code) {
            String token;
            try {
                // 通过code获取access_token
                token = WechatUtil.getUserAccessToken(code);

                System.out.println("token = " + token);

                ObjectMapper objectMapper = new ObjectMapper();
                HashMap<String,String> hashMap = objectMapper.readValue(token, HashMap.class);
                System.out.println("hashMap = " + hashMap);


                String refreshToken = hashMap.get("refresh_token");
                System.out.println("refreshToken = " + refreshToken);

                String openid = hashMap.get("openid");
                System.out.println("openid = " + openid);
                String url="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx0a03cef7d9802ef9&secret=0e01d9ac45803832f756c71c121b667b";
                String s = WechatUtil.httpsRequest(url, "GET", null);
                String accessToken = hashMap.get("access_token");
                System.out.println("accessToken = " + accessToken);
                HashMap<String,String> map = objectMapper.readValue(s, HashMap.class);
                System.out.println("s = " + s);


                // 通过access_token和openId获取用户昵称等信息
                user = WechatUtil.getUserInfo(accessToken, openId);
                System.out.println("user = " + user);

                request.getSession().setAttribute("openId", openId);
            } catch (IOException e) {
                log.error("error in getUserAccessToken or getUserInfo or findByOpenId: " + e.toString());
                e.printStackTrace();
            }
        }
        // ======todo begin======
        // 前面咱们获取到openId后,可以通过它去数据库判断该微信帐号是否在我们网站里有对应的帐号了,
        // 没有的话这里可以自动创建上,直接实现微信与咱们网站的无缝对接。
        // ======todo end======
        if (user != null) {
            // 获取到微信验证的信息后返回到指定的路由(需要自己设定)
            return "frontend/index";
        } else {
            return null;
        }
    }

/**
* 生成 url 发送到微信内部
*/
    public static void main(String[] args) throws UnsupportedEncodingException {
        String encode = URLEncoder.encode("http://itzgm.cn/wechatlogin/logincheck","UTF-8");
        StringBuffer buffer = new StringBuffer();

        buffer.append(" https://open.weixin.qq.com/connect/oauth2/authorize?")
                .append("appid=").append("wx0a03cef7d9802ef9").append("&redirect_uri=").append(encode)
                .append("&response_type=code&scope=snsapi_base&state=45897852#wechat_redirect");

        String s = buffer.toString();
        System.out.println("s = " + s);
    }

}

发送微信 API 的工具类

package org.javaboy.wechat_login.utils;

import org.javaboy.wechat_login.manager.MyX509TrustManager;
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 java.io.*;
import java.net.ConnectException;
import java.net.URL;

/**
 * 描述:
 *
 * @Author: YLq
 * @DateTime: 2021/7/16 16:09
 * @Version 1.0
 **/
public class WechatUtil {

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

    /**
     * 获取UserAccessToken实体类
     * @param code
     * @return
     * @throws IOException
     */
    public static String getUserAccessToken(String code) throws IOException {
        // 测试号信息里的appId
        String appId = "wx0a03cef7d9802ef9";
        log.debug("appId:" + appId);
        // 测试号信息里的appsecret
        String appsecret = "0e01d9ac45803832f756c71c121b667b";
        log.debug("secret:" + appsecret);
        // 根据传入的code,拼接出访问微信定义好的接口的URL
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret
                + "&code=" + code + "&grant_type=authorization_code";
        // 向相应URL发送请求获取token json字符串
        String tokenStr = httpsRequest(url, "GET", null);
        return tokenStr;
    }

    /**
     * 获取WechatUser实体类
     * @param accessToken
     * @param openId
     * @return
     */
    public static String getUserInfo(String accessToken, String openId) {
        // 根据传入的accessToken以及openId拼接出访问微信定义的端口并获取用户信息的URL
        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId
                + "&lang=zh_CN";
        // 访问该URL获取用户信息json 字符串
        String userStr = httpsRequest(url, "GET", null);
        log.debug("user info :" + userStr);
        return userStr;
    }

    /**
     * 发起https请求并获取结果
     * @param requestUrl
     *            请求地址
     * @param requestMethod
     *            请求方式(GET、POST)
     * @param outputStr
     *            提交的数据
     * @return json字符串
     */
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        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();
            log.debug("https buffer:" + buffer.toString());
        } catch (ConnectException ce) {
            log.error("Weixin server connection timed out.");
        } catch (Exception e) {
            log.error("https request error:{}", e);
        }
        return buffer.toString();
    }
}

MyX509TrustManager 主要继承 X509TrustManagerhttps 证书信任管理器

package org.javaboy.wechat_login.manager;

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

/**
 * 描述:
 *
 * @Author: YLq
 * @DateTime: 2021/7/16 16:12
 * @Version 1.0
 **/
public class MyX509TrustManager implements 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;
    }
}

测试结果:
在这里插入图片描述
这里我们便能获取到关注公众号人的信息了。但是我这里测试了一下在 第一步:用户同意授权scope的值是 snsapi_userinfo 的时候,到了 第四步:拉取用户信息 才能拉取到,而scope的值是 snsapi_base 的时候则获取不到详细信息。

最后,在自己测试的时候,不要忘记修改成为你自己的 appIDappsecret,还有最重要的就是域名。如上信息,仅供参考!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光头小小强007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值