昨天在写一个手机版的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找不到异常,第二次进来就好了,也没有其他报错信息,期待有大佬路过指正问题原因,感激不尽!
这之间其他的一些问题异常和容易碰到的坑以后有时间再总结!