前提准备
域名
开发微信网页授权时需要一个外网可以访问的域名,因为用户确认进行微信网页授权后微信服务器会通过一个回调URL向开发服务器发送一个回调请求。
开发阶段可以使用一些内网穿透工具来实现,例如:natapp、花生壳等等。
福利:natapp和花生壳都会免费赠送一些隧道。
注意:natapp提供的免费隧道每次启动客户端时产生的域名时随机的。
填坑:利用花生壳提供的域名进行内网穿透时可能会被微信拦截,所以推荐使用natapp(PS: 请测有效)。
个人订阅号
由于是进行微信网页授权,所以需要一个个人微信订阅号作为开发基础。
福利:由于微信个人订阅号没有提供微信网页授权功能,但是可以利用微信提供的测试号进行开发。
测试号在哪里:登录个人订阅号 -> 开发 -> 开发者工具 -> 公众平台测试账号
注意:整个开发过程中都是使用测试号的appID和appsecret
开发环境
JDK: 1.8
MAVEN: 3.x
SpringBoot: 2.x
IDEA: 2017专业版
微信网页授权官方文档
文档路径
文档在哪里:登录微信订阅号 -> 开发 -> 开发者工具 -> 开发文档 -> 微信网页开发 -> 微信网页授权
回调域名配置
在哪里配置:登录个人订阅号 -> 开发 -> 开发者工具 -> 公众平台测试账号 -> 网页服务 -> 网页账号 -> 修改 -> 填入授权回调页面域名即可(推荐使用natapp)
开发步骤
1 第一步:用户同意授权,获取code
2 第二步:通过code换取网页授权access_token
3 第三步:刷新access_token(如果需要)
4 第四步:拉取用户信息(需scope为 snsapi_userinfo)
5 附:检验授权凭证(access_token)是否有效
Java代码实现
进入微信授权页面
只需要通过微信浏览器访问到微信授权的页面即可。
思路
01 用户通过微信浏览器进入一个页面
02 点击一个按钮向开发者后台发送一个GET请求
03 开发这个后台封装一个URL并重定向到这个URL
代码片段
/** * 微信网页授权逻辑入口 * @param map */ @GetMapping(value = "/auth") public void toAuth(ModelMap map, HttpServletRequest request, HttpServletResponse response) throws IOException { log.info("进入微信授权逻辑......"); // 微信网页授权第一步 - 用户同意授权,获取code - start String getCodeUrl = weixinBaseInfoProperties.getAuth().getGetCodeUrl(); String appid = weixinBaseInfoProperties.getAppid(); String appsecret = weixinBaseInfoProperties.getAppsecret(); String redirectUri = weixinBaseInfoProperties.getAuth().getRedirectUri(); String scope = weixinBaseInfoProperties.getAuth().getScope(); getCodeUrl = getCodeUrl.replace("APPID", appid) .replace("REDIRECT_URI", redirectUri) .replace("SCOPE", scope); log.info("封装好的getCodeUrl为:" + getCodeUrl); response.sendRedirect(getCodeUrl); // 微信网页授权第一步 - 用户同意授权,获取code - end }
获取CODE和AccessToken、用户openid、用户信息
微信用户通过微信浏览器确认授权后,微信服务器会通过之前设定的回调URL向开发者后台发送一个GET请求,这个请求中携带了CODE信息。
思路
01 用户确认授权
02 微信服务器发送回调请求
03 开发服务器接收到回调请求
04 获取CODE
05 通过CODE在拼装一个url去请求微信服务器来获取access_token、openid
06 通过access_token、openid封装一个url去请求微信服务器来获取用户信息
微信网页授权的使用场景
直接利用微信账号作为网页账号
获取用户信息成功后直接跳转到目标页面即可
微信账号和网站账号进行绑定
思路:
01 获取用户信息成功
02 根据用户openid到数据库中去查找网页账号信息
03 如果查找到信息就说明已经绑定,直接跳转拿到目标页面即可
04 如果没有获取到就跳转到登录绑定页面
05 用户输入账号和密码并提交到开发者后台
06 后台需对网站账号合法性进行校验
07 校验通过后进行绑定操作
08 跳转到目标页面
代码片段一
/** * 回调页面 * @param map * @return */ @GetMapping(value = "/callback") public String callback(ModelMap map, HttpServletRequest request, HttpServletResponse response) { log.info("进入回调处理逻辑......"); String appid = weixinBaseInfoProperties.getAppid(); String appsecret = weixinBaseInfoProperties.getAppsecret(); // 微信网页授权第二步 - 通过code换取网页授权access_token - start StringBuffer requestURL = request.getRequestURL(); log.info("进入回调处理逻辑的请求url为:" + requestURL); String code = request.getParameter("code"); log.info("获取到的用于换取access_token的票据的code值为:" + code); String getAccessTokenUrl = weixinBaseInfoProperties.getAuth().getGetAccessTokenUrl(); getAccessTokenUrl = getAccessTokenUrl.replace("APPID", appid) .replace("SECRET", appsecret) .replace("CODE", code); log.info("封装好的用于获取accessToke的url为:" + getAccessTokenUrl); String getAccessTokenResponseStr = httpUtils.doGetStrByRestTemplate(getAccessTokenUrl); log.info("发送获取access_token请求后的响应为STR:" + getAccessTokenResponseStr); GetAuthAccessTokenResponse getAuthAccessTokenResponse = transformerUtils.String2Object(getAccessTokenResponseStr, GetAuthAccessTokenResponse.class); log.info("发送获取access_token请求后的响应为:" + getAuthAccessTokenResponse); String access_token = getAuthAccessTokenResponse.getAccess_token(); String open_id = getAuthAccessTokenResponse.getOpenid(); // 微信网页授权第二步 - 通过code换取网页授权access_token - end // 微信网页授权第四步 - 拉取用户信息(需scope为 snsapi_userinfo) - start String getUserInfoUrl = weixinBaseInfoProperties.getAuth().getGetUserInfoUrl(); getUserInfoUrl = getUserInfoUrl.replace("ACCESS_TOKEN", access_token) .replace("OPENID", open_id); log.info("封装好的用于获取userInfo的url为:" + getUserInfoUrl); String getUserInfoResponseStr = httpUtils.doGetStrByRestTemplate(getUserInfoUrl); log.info("发送获取userInfo请求后的响应为STR:" + getUserInfoResponseStr); GetUserInfoResponse getUserInfoResponse = transformerUtils.String2Object(getUserInfoResponseStr, GetUserInfoResponse.class); log.info("发送获取userInfo请求后的响应为:" + getUserInfoResponse); // 微信网页授权第四步 - 拉取用户信息(需scope为 snsapi_userinfo) - end // 第一种使用:直接使用微信的用户用户体系 // map.addAttribute("userinfo", getUserInfoResponse); // return "index2"; // 第二种:微信用户和网站用户进行绑定 // 根据openid查询网站的账户信息 UserBindDO userBindDOByOpenid = userBindRepository.findByOpenid(getUserInfoResponse.getOpenid()); if (userBindDOByOpenid != null) { // 已经绑定过的情况 log.info("已经绑定过啦"); map.addAttribute("userinfo", userBindDOByOpenid); return "index3"; } else { // 没有绑定过的情况 log.info("还未进行绑定操作"); map.addAttribute("openid", getUserInfoResponse.getOpenid()); map.addAttribute("nickname", getUserInfoResponse.getNickname()); return "login"; } }
代码片段二
@PostMapping(value = "/login") public String login(UserBindDO userBindDO, ModelMap map) { log.info("获取到的参数信息为:" + userBindDO); // TODO: 对账户信息进行格式校验,如果格式不正确直接返回登录绑定页面 // TODO: 验证账户是否存在, 如果不存在直接跳转到注册页面(微信用户信息同时返回),在注册逻辑中直接进行绑定 // TODO: 验证账户密码是否正确,如果部正确直接返回登录绑定页面 UserBindDO save = userBindRepository.save(userBindDO); if (save != null) { log.info("bind - 绑定成功"); log.info("绑定后的结果信息为:" + save); map.addAttribute("userinfo", save); return "index3"; } else { log.info("bind - 绑定失败"); return "login"; } }
源代码
扫码获取