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找不到异常,第二次进来就好了,也没有其他报错信息,期待有大佬路过指正问题原因,感激不尽!


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

目录
相关文章
|
1月前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
65 7
|
1月前
|
Android开发 开发者 Python
手撸了一个全自动微信清粉小工具(源码详解)
微信清理僵尸粉工具利用Python和`uiautomator2`库,通过模拟转账操作自动检测并标记微信好友列表中被删除、被拉黑或账号存在问题的“僵尸粉”。工具支持开启调试模式、自动打开微信、获取好友信息、判断好友状态、管理标签等功能,最终将检测结果记录到文件中,便于用户管理和清理好友列表。此工具适用于Android设备,已在OPPO Reno4 Pro上测试成功。
94 5
|
1天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
25天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
110 13
|
2月前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
58 12
|
1月前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
1月前
|
人工智能 移动开发 安全
家政上门系统用户端、阿姨端源码,java家政管理平台源码
家政上门系统基于互联网技术,整合大数据分析、AI算法和现代通信技术,提供便捷高效的家政服务。涵盖保洁、月嫂、烹饪等多元化服务,支持多终端访问,具备智能匹配、在线支付、订单管理等功能,确保服务透明、安全,适用于家庭生活的各种需求场景,推动家政市场规范化发展。
|
安全 Java
Java并发编程笔记之CopyOnWriteArrayList源码分析
并发包中并发List只有CopyOnWriteArrayList这一个,CopyOnWriteArrayList是一个线程安全的ArrayList,对其进行修改操作和元素迭代操作都是在底层创建一个拷贝数组(快照)上进行的,也就是写时拷贝策略。
19559 0
|
Java 安全
Java并发编程笔记之读写锁 ReentrantReadWriteLock 源码分析
我们知道在解决线程安全问题上使用 ReentrantLock 就可以,但是 ReentrantLock 是独占锁,同时只有一个线程可以获取该锁,而实际情况下会有写少读多的场景,显然 ReentrantLock 满足不了需求,所以 ReentrantReadWriteLock 应运而生,ReentrantReadWriteLock 采用读写分离,多个线程可以同时获取读锁。
3143 0
|
Java
Java并发编程笔记之FutureTask源码分析
FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
4302 0