java实现微信第三方登录流程源码详解,所遇到的坑

简介: java实现微信第三方登录流程源码详解,所遇到的坑

昨天在写一个手机版的web项目,要在微信客户端使用微信第三方登录,想着也没啥,看看就看看吧,但是可恶的微信官方文档给我画了很大的一个坑,特此记录

二次更新,说明一下我这个是用我微信网页授权,就是在微信客户端点开链接或菜单能弹出微信授权登录框的功能!

前期准备工作我就不多说了,无非就是公众平台账号,填写相关资料耐心等待审核就好。

这里要注意一点的是,网站应用创建好之后的授权回调域填写顶级域名就好,之前我一直写的二级域名,测试的时候回调总是过不来,后来回来看文档,微信说的就是,该域名下的所有页面都 可以回调

创建好网站应用之后我们来看微信提供的接口文档

微信开放平台第三方登录接口文档地址

第一步:请求CODE

根据官方文档,请求code这里要按照响应的参数进行拼接,参数就按照官方提供的,需要注意的是appid是你用哪个公众号登录就用哪个公众号的appid,这里不是开放平台的网站应用appid


然后另一个坑,redirect_uri,一定要用urlEncode对链接进行处理,这个链接是用户打开这个链接同意登录之后会跳转的地址,我们要跳转到后台对回调的信息就行处理,所以就要回调到我们域名下的controller控制器方法中,并且一定是要外网可访问。比如:www.test.com/callback

把链接进行Encode处理,这里提供一个 encode在线解码工具


拼接好路径之后再微信客户端打开就应该会显示某某公众号的授权登录页面了,如果报错那就是路径没有拼接正确


请求code的控制器如下所示:

参数是我自己的,换成你的参数就好

    @RequestMapping("/getCode")
    public void getCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //拼接url
        StringBuilder url = new StringBuilder();
        url.append("https://open.weixin.qq.com/connect/oauth2/authorize?");
        //微信开放平台的appid
        url.append("appid=" + WeixinConfig.appId);
        //转码
        try {
          //回调地址 ,回调地址要进行Encode转码
            String redirect_uri = URLEncoder.encode(WeixinConfig.REDIRECT_URI, "utf-8");
            System.out.println("redirect_uri==" + redirect_uri);
            url.append("&redirect_uri=" + redirect_uri);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        url.append("&response_type=code");
        url.append("&scope=snsapi_userinfo");
        url.append("&state=" + request.getSession().getId());
        url.append("#wechat_redirect");
        System.out.println("url===" + url.toString());
//        return "redirect:" + url.toString();
        String s = url.toString();
        response.sendRedirect(s);
    }

返回说明


用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数


第二步:通过code换取网页授权access_token


第三步:刷新access_token(如果需要)


第四步:拉取用户信息(需scope为 snsapi_userinfo)


这三步我们应该在上面授权回调方法中调用,起初我在想每一个方法都要返回一个json,那么是不是我要三个控制器方法才行?,怎样才能在一个控制器里调用三个方法呢?


感谢大神提供的发送GET请求的工具类:

public static JSONObject doGetJson(String url) throws Exception, IOException {
        JSONObject jsonObject = null;
        //初始化httpClient
        DefaultHttpClient client = new DefaultHttpClient();
        //用Get方式进行提交
        HttpGet httpGet = new HttpGet(url);
        //发送请求
        HttpResponse response = client.execute(httpGet);
        //获取数据
        HttpEntity entity = response.getEntity();
        //格式转换
        if (entity != null) {
            String result = EntityUtils.toString(entity, "UTF-8");
            jsonObject = JSONObject.fromObject(result);
        }
        //释放链接
        httpGet.releaseConnection();
        return jsonObject;
    }

最后附上我的授权回调方法:

  /**
     * 微信 授权登录回调
     **/
    @RequestMapping("/callback")
    public void callback(String code, String state, HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("====" + code + "===" + state + "====");
        logger.debug("code===" + code);
        logger.debug("state===" + state);
        if (StringUtils.isNotEmpty(code)) {
            logger.debug("sssssssss====" + code);
            StringBuilder url = new StringBuilder();
            url.append("https://api.weixin.qq.com/sns/oauth2/access_token?");
            //微信公众平台的AK和SK
            url.append("appid=" + WeixinConfig.appId);
            url.append("&secret=" + WeixinConfig.appSecret);
            //这是微信回调给你的code
            url.append("&code=" + code);
            url.append("&grant_type=authorization_code");
            System.out.println("url.toString()===" + url.toString());
            logger.debug("url.toString()===" + url.toString());
            JSONObject jsonObject = AuthUtil.doGetJson(url.toString());
            logger.debug("jsonObject================"+jsonObject);
            //解析jsonStr的字符串
            //1.获取微信用户的openid
            String openid = jsonObject.getString("openid");
            //2.获取获取access_token
            String access_token = jsonObject.getString("access_token");
            logger.debug("openid===" + openid);
            logger.debug("access_token===" + access_token);
            String infoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid
                    + "&lang=zh_CN";
            logger.debug("infoUrl===" + infoUrl);
            //3.获取微信用户信息
            JSONObject userInfo = AuthUtil.doGetJson(infoUrl);
            logger.debug("userInfo======================"+userInfo);
            //至此拿到了微信用户的所有信息,剩下的就是业务逻辑处理部分了
            //保存openid和access_token到session
            if (openid==null){
                logger.debug("-------------------------微信授权回调,获取用户信息失败!=============================");
                response.sendRedirect("http://m.huerdai.net/html/Program-error.html");
                return;
            }
            request.getSession().setAttribute("openid", openid);
            request.getSession().setAttribute("access_token", access_token);
            logger.debug("openid===" + openid);
            logger.debug("access_token===" + access_token);
            String sessionid = getRequest().getSession().getId();
            //去数据库查询有没有这个 openid
            CustomerInfo customerInfoServiceOne = iCustomerInfoService.getOne(new QueryWrapper<CustomerInfo>().eq("openid", openid));
            //如果没有这一个用户,则创建
            if (customerInfoServiceOne == null) {
                CustomerInfo customerInfo = new CustomerInfo();
                //省略实体set方法
                boolean save = registerService.register(customerInfo);
                if (save) {
                    logger.debug("首次认证:http://m.huerdai.net");
                    redisTemplate.opsForValue().set(sessionid, customerInfoServiceOne.getCustomerId());
//                    response.sendRedirect("http://m.huerdai.net/index.html");
                    response.sendRedirect("http://m.huerdai.net/html/bind-tel.html");
                    return;
                } else {
                    logger.debug("认证失败!");
                    response.sendRedirect("http://m.huerdai.net/error.html");
                    return;
                }
            } else {
                //已经授权过,没有绑定手机号,也是直接跳转到首页
                redisTemplate.opsForValue().set(sessionid, customerInfoServiceOne.getCustomerId());
                if (customerInfoServiceOne.getMobilePhone() == null) {
                    logger.debug("已经授权过,没有绑定手机号,也是直接跳转到首页");
                    //并且将用户信息存到Redis中
//                    response.sendRedirect("http://m.huerdai.net/index.html");
                    response.sendRedirect("http://m.huerdai.net/html/bind-tel.html");
                    return;
                } else {
                    //已经授权过,并且已经绑定手机号
                    logger.debug("有openid的跳转http://m.huerdai.net222222");
                    response.sendRedirect("http://m.huerdai.net/index.html");
                    return;
                }
            }
        } else {
            logger.debug("code获取失败!====" + code);
            // return new ModelAndView("redirect:http://m.huerdai.net/error.html");
            response.sendRedirect("http://m.huerdai.net/error.html");
        }
    }

到这里就微信登录并且获取用户信息就算完成了,


然后我遇到一个问题就是用户首次登录进来的时候会报openid找不到异常,第二次进来就好了,也没有其他报错信息,期待有大佬路过指正问题原因,感激不尽!


这之间其他的一些问题异常和容易碰到的坑以后有时间再总结!

目录
相关文章
|
30天前
|
JavaScript 前端开发 Java
|
1月前
|
存储 数据可视化 Java
【Java】Java swing 民宿管理系统 GUI(源码+可视化界面)【独一无二】
【Java】Java swing 民宿管理系统 GUI(源码+可视化界面)【独一无二】
|
12天前
|
Kubernetes jenkins 持续交付
从代码到k8s部署应有尽有系列-java源码之String详解
本文详细介绍了一个基于 `gitlab + jenkins + harbor + k8s` 的自动化部署环境搭建流程。其中,`gitlab` 用于代码托管和 CI,`jenkins` 负责 CD 发布,`harbor` 作为镜像仓库,而 `k8s` 则用于运行服务。文章具体介绍了每项工具的部署步骤,并提供了详细的配置信息和示例代码。此外,还特别指出中间件(如 MySQL、Redis 等)应部署在 K8s 之外,以确保服务稳定性和独立性。通过本文,读者可以学习如何在本地环境中搭建一套完整的自动化部署系统。
38 0
|
23天前
|
JSON 小程序 JavaScript
微信小程序制作 购物商城首页 【内包含源码】
这篇文章提供了一个微信小程序购物商城首页的实现方法和源码,包括页面布局、数据结构、核心代码以及如何配置tabBar和搜索框组件。
微信小程序制作 购物商城首页 【内包含源码】
|
16天前
|
运维 Cloud Native Java
Java项目部署的发展流程
本文对比分析了四种不同的应用部署方式:传统部署、虚拟化部署、容器化部署及云原生部署。传统部署直接在物理机上运行程序,存在资源复用难等问题。虚拟化部署通过虚拟机技术实现了资源的有效隔离与利用,但可能会造成性能损失。容器化部署则进一步提升了应用的可移植性和资源利用率,减轻了运维负担。云原生部署结合容器化、微服务等技术,实现了应用的快速迭代、高效运维和灵活扩展,适用于现代互联网应用的开发与部署。每种方式均针对其特点进行了详细的流程描述与优缺点分析。
|
24天前
|
Java
在Java编程的广阔天地中,条件语句是控制程序流程、实现逻辑判断的重要工具。
在Java编程中,if-else与switch作为核心条件语句,各具特色。if-else以其高度灵活性,适用于复杂逻辑判断,支持多种条件组合;而switch在多分支选择上表现优异,尤其适合处理枚举类型或固定选项集,通过内部跳转表提高执行效率。两者各有千秋:if-else擅长复杂逻辑,switch则在多分支选择中更胜一筹。理解它们的特点并在合适场景下使用,能够编写出更高效、易读的Java代码。
24 1
|
22天前
|
存储 安全 Java
【Java 第四篇章】流程控制、容器
本文档详细介绍了Java中的流程控制、集合类型、数组声明及容器的声明与遍历等内容。在流程控制部分,包括了if、if...else、if...else if...else、switch等语句的使用方法,并提供了具体示例。接着,文档对比分析了Java中单列集合(如HashSet、LinkedHashSet、TreeSet等)与双列集合(如HashMap、LinkedHashMap、Hashtable等)的特点及底层实现原理。此外,还介绍了如何声明与初始化数组,并提供了多种循环结构的使用示例。最后,通过具体的代码示例展示了不同集合类型的声明、基本操作(如添加、删除、更新、查找)以及遍历方法。
11 0
|
26天前
|
算法 安全 Java
深入解析Java多线程:源码级别的分析与实践
深入解析Java多线程:源码级别的分析与实践
|
30天前
|
前端开发 JavaScript API
微信公众号项目,实现微信支付(具体流程和参数)
微信公众号项目,实现微信支付(具体流程和参数)