一、主要流程

实现第三方登录,从微信获取用户信息,用微信公众平台和微信开放平台,其实流程和原理都是一样的,就是调用的接口和对应的参数有点区别。

微信公众平台 和 微信开放平台 对应的官方文档如下:

微信开放文档

准备工作 | 微信开放文档

本次用 公众平台 来测试,主要流程如下:

1、重定向微信接口,微信扫码确认

点击“微信登录”按钮,系统接口返回一个跳转地址(地址微信公众平台提供的,部分参数需要拼接,公众平台的接口只能用微信打开)如下,参数的详细介绍可以参考官网:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

注意:

1.redirect_uri后面携带的参数是微信的回调地址,用户点击“同意”后,微信会请求这个地址,需要用 URLEncoder.encode(path,"utf-8") 进行编码

2、微信扫码确认后,调用回调接口

用手机微信访问上面返回的完整接口后,会提示是否同意登录,点击确认后,微信端会回调第一步中,接口里面设置的回调地址,即redirect_uri,并且携带code和state参数,如下:

redirect_uri?code=CODE&state=STATE

回调方法中,需要获取code以及state

注意:

1.验证当前state是否和跳转微信接口传递的state一致,来防止csrf攻击(可以不进行验证)

2.可以通过state来传递我们自己需要的参数

举例:设置的回调函数为:redirect_uri=http://www.aaa.com/getWeChatUserInfo

那么需要在后台定义一个接口,用来接受微信的回调,如下:

    @RequestMapping("/getWeChatUserInfo")public String getWeChatUserInfo(HttpServletRequest request){//校验stateString state = request.getParameter("state");//微信返回的code授权码String code = request.getParameter("code");//请求tokenWeChatAccessToken token = weChatLoginService.getAccessTokenByCode(code);//根据token获取用户信息WeChatUserInfo userInfo = weChatLoginService.getWeixinUserInfo(token);System.out.println(JSON.toJSONString(userInfo));/*** 可以在此处,根据业务需求开发自己的功能*///重定向到其他页面return "redirect:http://www.baidu.com";}

3、根据code获得微信的access_token

上一步获取code后,携带code和appId等参数,调用微信接口,获得access_token,微信接口:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

返回结果如下:

{"access_token": "52_mgvyGIVQGJoVw-JUo-3GeCQggL5g11rtduGu9lwMOp5sZOIanEVH9rpuabAu9oo8nu89TcZszwz10eRq5DDDdoIuXZGPyfMEdcynGP1gS0U","expires_in": 7200,"refresh_token": "52_K6X9KzoYPlTUem-MCH05X0KCIzikZy-UiX4XdxhSW-pUgWjlAVn8BDGTfwmQ4mk1Bdhd0-SSxmP1f5HQYecxWBdcGLa7UhFer1pvDzUs1gU","openid": "oGy6Y5tp9qxP5XLBvRU2OCIxFdvQ","scope": "snsapi_userinfo"
}

4、获取到token后,再去调用微信接口,获取微信端用户的信息

https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID

返回结果如下:

{"openid": "oGy6Y5tp9qxP5XLBvRU2OCIxFdvQ","nickname": "A→枫火","sex": 0,"language": "","city": "","province": "","country": "","headimgurl": "https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKr27Hpfq957HPIXHoReLn1OvVXWFchNdW1UiaicBOugzz5ax7Hn8VzGceC50NRRQ02ibhA4ibFXZX3zg/132","privilege": []
}

二、准备工作

微信公众平台提供测试账号,而开放平台必须要有开发者资质认证,所以用公众平台来测试

1、获取APPID和appsecret

上面提到的一些参数,appId和appsecret,是需要获取的,打开链接,扫码登录

微信公众平台

2、 关注公众号

公众平台,转发的请求地址,只能用手机微信打开,而访问请求,必须先关注下面的测试公众号

3、设置微信回调域名

通过:网页服务-网页帐号-网页授权获取用户基本信息,点击修改,进行回调域名的配置

设置回调函数的时候,域名必须要和这里设置的域名相同

4、设置花生壳的内网穿透

因为在设置微信回调接口的时候,微信端是要访问我们设置的回调接口的,回调接口中的域名要和第三步中设置的域名保持一致

因此使用花生壳设置内网穿透,保证手机微信在调用此域名的时候,能够访问我们电脑端上的服务

三、代码实现

1、先创建一个springboot项目,结构如下

核心的代码是:ThirdLoginWeChatController 和 WeChatLoginService,其他的都是配置和工具类

2、具体对应的代码

application.yml,配置信息,

server:port: 9200
oauth:weixin:#微信公众平台的应用idappID: #微信公众平台的应用秘钥appsecret: #获取授权码的URLauthorizeUrl: https://open.weixin.qq.com/connect/oauth2/authorize#获取令牌的URLtokenUrl: https://api.weixin.qq.com/sns/oauth2/access_token#获取用户信息的URLuserInfoUrl: https://api.weixin.qq.com/sns/userinfo#回调地址的URLbackUrl: http://273mc88979.zicp.vip/wechat/getWeChatUserInfo

WeChatConfig,配置类,获取并封装配置文件中的信息,包括APPID,appsecret,访问微信的接口和回调域名等

@Data
@Configuration
@ConfigurationProperties(prefix = "oauth.weixin")
public class WeChatConfig {//微信公众平台的应用idprivate String appID;//微信公众平台的应用秘钥private String appsecret;//获取授权码的URLprivate String authorizeUrl;//获取令牌的URLprivate String tokenUrl;//获取用户信息的URLprivate String userInfoUrl;//回调地址的URLprivate String backUrl;
}

WeChatConstant,定义一些常量,主要是发送微信请求所需要的参数常量

@Data
public class WeChatConstant {public static final String SNSAPI_USERINFO = "snsapi_userinfo";public static final String RESPONSE_TYPE_CODE = "code";public static final String AUTHORIZATION_CODE = "authorization_code";//作为key,用来在redis中保存statepublic static final String STATE_KEY = "state_key";
}

WeChatAccessToken,实体类,封装微信返回的accesstoken

@Data
public class WeChatAccessToken {//授权用户唯一标识private String openid;//接口调用凭证private String access_token;//用户刷新access_tokenprivate String refresh_token;//用户授权的作用域,使用逗号(,)分隔private String scope;//凭证超时时间,单位(秒)private String expires_in;//当且仅当该网站应用已获得该用户的userinfo授权时,才会出现该字段。private String unionid;
}

WeChatUserInfo,实体类,封装微信返回的用户信息

@Data
public class WeChatUserInfo {//授权用户唯一标识private String openId;//昵称private String nickName;//性别,1:男,2:女,0:未知private String sex;//头像地址private String headImgUrl;//国籍private String country;//省private String province;//城市private String city;//语言private String language;//特权private String privilege;//关联内部用户的idprivate String systemUserId;//关联内部用户的用户名private String systemUserName;
}

ThirdLoginWeChatController,controller,对外提供的访问接口和微信回调接口

@Controller
@RequestMapping("/wechat")
public class ThirdLoginWeChatController {@AutowiredWeChatLoginService weChatLoginService;/*** 点击第三方登录,使用微信登录* 跳转到微信扫码页面*/@GetMapping("/toLogin")@ResponseBodypublic String login(){//获取跳转的扫码地址String redirectUrl = weChatLoginService.getWeixinQRUrl();return redirectUrl;}/*** 回调函数,用户扫码同意后,跳转到该接口,并携带code和state* 1.获取code和state* 2.通过code获取token* 3.再根据token获取微信端的用户信息*/@RequestMapping("/getWeChatUserInfo")@ResponseBodypublic String getWeChatUserInfo(HttpServletRequest request){//校验stateString state = request.getParameter("state");//微信返回的code授权码String code = request.getParameter("code");System.out.println("===================code:"+request.getParameter("code"));System.out.println("===================state:"+request.getParameter("state"));//从redis获取state信息,与回调接受的state对比,可用于防止csrf攻击(跨站请求伪造攻击)
//        String realState = redisService.getCacheObject(WeChatConstant.STATE_KEY);
//        if(!state.equals(realState)){
//            throw new ServiceException("微信回调地址,返回的state不一致");
//        }//请求tokenWeChatAccessToken token = weChatLoginService.getAccessTokenByCode(code);//根据token获取用户信息WeChatUserInfo userInfo = weChatLoginService.getWeixinUserInfo(token);System.out.println(JSON.toJSONString(userInfo));/*** 可以在此处,根据业务需求开发自己的功能* 如:关联本地账户,用来实现登录逻辑*      1.本地有账户,直接登录,返回token*      2.本地没有账户,需要跳转页面,用户注册后绑定微信*/return JSON.toJSONString(userInfo);//重定向到其他页面//return "redirect:http://www.baidu.com";}
}

WeChatLoginService,service,用来获取token和用户信息等业务操作

@Service
public class WeChatLoginService {@AutowiredWeChatConfig authWinxinConfig;/*** 获取第三方登录页面,即显示微信扫码页面的地址*/public String getWeixinQRUrl(){//String redirectUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";//处理一下回调地址String backUrl = null;try {String path = authWinxinConfig.getBackUrl();backUrl = URLEncoder.encode(path,"utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}//生成随机的UUID,作为state,微信平台回调本地接口时,会携带此参数,可以校验此参数,可用于防止csrf攻击(跨站请求伪造攻击)
//        String state = UUID.randomUUID().toString();
//        redisService.setCacheObject(WeChatConstant.STATE_KEY,state);//拼接跳转的扫码地址StringBuffer redirectUrl = new StringBuffer();redirectUrl.append(authWinxinConfig.getAuthorizeUrl()).append("?appid=").append(authWinxinConfig.getAppID()).append("&redirect_uri=").append(backUrl).append("&scope=").append(WeChatConstant.SNSAPI_USERINFO).append("&response_type=").append(WeChatConstant.RESPONSE_TYPE_CODE).append("&state=").append("123").append("#wechat_redirect");return redirectUrl.toString();}/*** 根据code获取token和openid*/public WeChatAccessToken getAccessTokenByCode(String code){//https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_codeStringBuffer tokenUrl = new StringBuffer();tokenUrl.append(authWinxinConfig.getTokenUrl()).append("?appid=").append(authWinxinConfig.getAppID()).append("&secret=").append(authWinxinConfig.getAppsecret()).append("&code=").append(code).append("&grant_type=").append(WeChatConstant.AUTHORIZATION_CODE);String result = HttpUtils.get(tokenUrl.toString());System.out.println(result);WeChatAccessToken token = JSON.parseObject(result, WeChatAccessToken.class);return token;}/*** 根据token和openID获取用户信息*/public WeChatUserInfo getWeixinUserInfo(WeChatAccessToken token){//https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENIDStringBuffer userInfoUrl = new StringBuffer();userInfoUrl.append(authWinxinConfig.getUserInfoUrl()).append("?access_token=").append(token.getAccess_token()).append("&openid=").append(token.getOpenid());String userStr = HttpUtils.get(userInfoUrl.toString());System.out.println(userStr);WeChatUserInfo weChartUserInfo = JSON.parseObject(userStr, WeChatUserInfo.class);return weChartUserInfo;}
}

HttpUtils,简单的工具类,封装了HttpClient方法的实现

public class HttpUtils<T> {/*** get请求返回JSONObject* @param url* @return*/public static JSONObject getJSON(String url){String resultStr = get(url);JSONObject res = JSONObject.parseObject(resultStr);return res;}/*** post请求返回JSONObject* @param url* @return*/public static JSONObject postJSON(String url, String jsonStr){String resultStr = post(url, jsonStr);JSONObject res = JSONObject.parseObject(resultStr);return res;}/*** 发送 get 请求** @param url 请求地址* @return 请求结果*/public static String get(String url) {String result = null;CloseableHttpResponse response = null;CloseableHttpClient httpclient = HttpClients.createDefault();try {// 创建uriURIBuilder builder = new URIBuilder(url);URI uri = builder.build();// 创建http GET请求HttpGet httpGet = new HttpGet(uri);// 执行请求response = httpclient.execute(httpGet);if (response.getStatusLine().getStatusCode() == 200) {result = EntityUtils.toString(response.getEntity(), "UTF-8");}} catch (Exception e) {e.printStackTrace();}return result;}/*** 发送 post 请求** @param url     请求地址* @param jsonStr Form表单json字符串* @return 请求结果*/public static String post(String url, String jsonStr) {// 创建httpClientCloseableHttpClient httpClient = HttpClients.createDefault();// 创建post请求方式实例HttpPost httpPost = new HttpPost(url);// 设置请求头 发送的是json数据格式httpPost.setHeader("Content-type", "application/json;charset=utf-8");httpPost.setHeader("Connection", "Close");// 设置参数---设置消息实体 也就是携带的数据StringEntity entity = new StringEntity(jsonStr, Charset.forName("UTF-8"));// 设置编码格式entity.setContentEncoding("UTF-8");// 发送Json格式的数据请求entity.setContentType("application/json");// 把请求消息实体塞进去httpPost.setEntity(entity);// 执行http的post请求CloseableHttpResponse httpResponse;String result = null;try {httpResponse = httpClient.execute(httpPost);result = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");} catch (IOException e) {e.printStackTrace();}return result;}
}

对接微信公众(开放)平台,获取微信用户信息,实现第三方登录相关推荐

  1. 蚂蚁开放平台—获取支付宝用户信息

    2019-07-08 新增 官方有个链接可以直接查看,直接支付宝APP扫码即可查看自己的userid: 开发流程:https://docs.open.alipay.com/284/106001/ (官 ...

  2. 微信公众开放平台开发01---微信公众平台介绍,以及开发准备

    微信公众开放平台开发01---微信公众平台介绍,以及开发准备 技术qq交流群:JavaDream:251572072 部分内容收集于网络: 1.先分享一份源码: 微信公众开放平台开发 -智能机器人ja ...

  3. 微信公众开放平台开发08---纯java 实现微信开发:编写自定义菜单

    微信公众开放平台开发08---纯java 实现微信开发:编写自定义菜单 微信公众开放平台开发08---纯java 实现微信开发:编写自定义菜单  技术qq交流群:JavaDream:251572072 ...

  4. 微信公众开放平台开发07---java servlet 实现微信开发第一步:微信服务器验证

    微信公众开放平台开发07---java servlet 实现微信开发第一步:微信服务器验证  技术qq交流群:JavaDream:251572072 ------------------------- ...

  5. 微信公众开放平台开发02---微信公众平台PHP接口和java接口对比

    微信公众开放平台开发02---微信公众平台PHP接口和java接口对比 技术qq交流群:JavaDream:251572072 ----------------------------------- ...

  6. 微信公众开放平台开发04---百度BAE,java应用部署服务器,jetty了解

    微信公众开放平台开发04---百度BAE,java应用部署服务器,jetty了解 技术qq交流群:JavaDream:251572072 1.Jetty 是一个开源的servlet容器,它为基于Jav ...

  7. 微信公众开放平台开发06---复习一下servlet

    微信公众开放平台开发06---复习一下servlet  技术qq交流群:JavaDream:251572072  ------------------------------------------- ...

  8. 微信公众开放平台开发03---百度BAE上搭建属于自己的微信公众平台 -JAVA,微信公众开放平台部署到百度云中BASE2.0,进行调试,木有钱买云服务器的亲们试试

    微信公众开放平台开发03---百度BAE上搭建属于自己的微信公众平台 -JAVA,微信公众开放平台部署到百度云中BASE2.0,进行调试,木有钱买云服务器的亲们试试 技术qq交流群:JavaDream ...

  9. 微信公众开放平台开发05---jetty部署异常:rg.apache.jasper.JasperException: PWC6345: There is an error in invoking ja

    微信公众开放平台开发05---jetty部署异常:rg.apache.jasper.JasperException: PWC6345: There is an error in invoking ja ...

  10. 百度云搭建微信公众平台服务器,微信大众开放平台开发03-百度BAE上搭建属于自己的微信公众平台 -JAVA,微信公众开放平台部署到百度云中BASE2.0,进行调试,木有钱买云服务器的亲们试试...

    微信公众开放平台开发03---百度BAE上搭建属于自己的微信公众平台 -JAVA,微信公众开放平台部署到百度云中BASE2.0,进行调试,木有钱买云服务器的亲们试试 微信公众开放平台开发03---百度 ...

最新文章

  1. 根据给定数据创建JSON并验证
  2. R语言使用ggplot2包使用geom_dotplot函数绘制分组点图(添加均值、中位数)实战(dot plot)
  3. 小程序在wxml里转数字_2020年利用名片小程序开启数字化营销的方法
  4. 图灵奖得主杨立昆:AI+时代,未来将会如何被改变
  5. window7环境下ZooKeeper的安装运行及监控查看
  6. 算法题-大数相乘问题
  7. MySQL(介绍,安装,密码操作,权限表)
  8. Android 系统(203)---Android包管理机制(一)PackageInstaller的初始化
  9. python 热力图_python高维数据型图表热力图、树形图
  10. go 协程回调函数 传入参数_ECMAScript 6 入门教程—Generator 函数的异步应用
  11. C语言 n的作业,C语言作业练习
  12. UVALive 5760 Alice and Bob
  13. golang清空切片
  14. css-3d旋转相册
  15. 第9章、图像按钮ImageButton(从零开始学Android)
  16. Android APP开发文档模板
  17. cesium获取经纬度
  18. VB.net是个弥天大谎,VB.net已死(海康威视 SDK 开发有感)
  19. Atom 修改Atom快捷键 实测可行
  20. Core Techniques And Algorithms In Game Programming

热门文章

  1. 详细了解一下股票量化交易接口股
  2. 机关事业单位考勤统计和活动抽奖小程序
  3. 电商供应链系统的DDD架构设计实战
  4. 检测网站CDN是否生效
  5. 简单聊聊为什么说外包不好?
  6. skype在线代码 skype在线代码怎么弄?
  7. ubuntu+网易云安装
  8. 如何在搜狗高速浏览器设置代理IP
  9. 程序员是做什么的?怎么成为程序员?
  10. 电子计算机出现的背景,世界第一台电子计算机产生的背景是什么