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

一、主要流程

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

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

微信开放文档

准备工作 | 微信开放文档

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

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;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/34566.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java-最新微信第三方平台公众号授权

第三方平台api地址 https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/预授权码获取之后调用接口获取授权方信息 https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/authorization_info.html#%E6%8E%88%E6%9D…

微软 Edge 浏览器怎样安装插件

微软 Edge 浏览器怎样安装插件 一、安装微软商店提供的插件二、安装第三方插件到Edge浏览器 之前安装插件就没有了解很深&#xff0c;放到浏览器里面就直接用了&#xff0c;最近再次想在微软的Edge浏览器安装插件的时候&#xff0c;发现没有之前的那么顺手&#xff0c;于是记录…

惊!最靠谱的谷歌与edge浏览器安装扩展插件方法

谷歌与edge浏览器安装扩展插件 谷歌浏览器安装扩展插件Edge浏览器安装扩展插件注意 谷歌浏览器安装扩展插件 1.在浏览器地址栏中输入: chrome://extensions/ 2.打开开发者模式&#xff0c;并点击加载已解压的扩展程序 3.选中下载好的文件夹feisou-assist导入即可&#xff…

电影解说文案开头模板

一个好的解说文案&#xff0c;开头几句必须精彩&#xff01;我们要解说一部影视作品&#xff0c;首先得自己先看一两遍&#xff0c;摸清故事情节&#xff0c;到底讲了一个什么故事&#xff0c;然后再结合我们对故事的理解&#xff0c;将故事讲给观众听。我们把文案分为开头、内…

修改Ubuntu国内镜像源地址

目录 方法一方法二方法三 方法一 Ubuntu可视化界面修改 在设置中的软件和更新中修改红框内容即可&#xff0c;修改后关闭会提示重启服务选择它即可 方法二 修改源文件 位置&#xff1a;/etc/apt/sources.list 首先备份以便出错后还原&#xff1a;sudo cp /etc/apt/sources.…

跟着GPT-4 从零完Python 爬虫

前言 先说个人情况&#xff1a;我作为产品经理自从 4 年前毕业很长时间都没有写过代码了&#xff0c;本科时候接触过一点 Python 的 慕课&#xff0c;但那个时候也是理论多于实操&#xff0c;为数不多跑通过的爬虫可能是豆瓣的电影 TOP 250&#xff1b;更多时候是被环境配置和…

Go 统计含 emoji 字符串字符数

1.背景 项目种需要统计用户昵称的字符数量进行限制&#xff0c;用户可以输入英文&#xff0c;中文&#xff0c;emoji 字符&#xff0c;当用户输入中英文和普通的 emoji 字符时&#xff0c;将字符串转为 []rune 进行统计没有问题。 func main() {s0 : "我爱中国" …

PostgreSQL中统计指定字符或者单词或者字符串在一个长字符串中出现总次数,PostgreSQL统计字符串中某字符出现次数

PostgreSQL中统计指定字符或者单词或者字符串在一个长字符串中出现总次数&#xff0c;PostgreSQL统计字符串中某字符出现次数 pg自带函数的方式另外一种思路方式&#xff0c;字符替换&#xff0c;统计被替换的字符数函数 translate(string text, from text, to text) pg自带函数…

PostgreSQL 字符串函数汇总

文章目录 前言拼接字符串填充字符串大小写转换获取字符串长度截取字符串裁剪字符串获取第一个字符的ASCII码计算string的MD5散列判断是否包含字符串null 和 的区别与判断以及COALESCE函数nullif函数合并字符串将字符串合并成一个数组分割字符串 总结 前言 本文基于 PostgreSQ…

婚礼视频mv短片制作,3分钟快速教程!教你制作婚礼开场创意视频

制作一个婚礼视频或婚礼MV短片,当下非常流行。用生活照、婚纱照片做成视频,在婚礼上当作开场或者生活中留给婚礼一个纪念,都是不错的选择。而且用照片做成视频,方法简单,但是创意依旧满满。 今天就教大家3分钟快速学会制作婚礼视频,利用生活照或婚纱照,配上数码大师里的…

SpringBoot+Redis实现接口限流

1.redis接口限流注解 定义一个注解标明需要使用限流的接口 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface AccessLimit {/*** return 单位时间&#xff08;秒&#xff09;*/int seconds();/*** return 单位时间最大请求次数*/…

访问url图片并上传oss图片显示不完整问题解决

问题&#xff1a;在之前通过链接上传图片的时候&#xff0c;都是先获取inputStream流&#xff0c;然后通过available()方法获取文件大小。但是通过这种方法获取到的文件大小是不准确的&#xff0c;因为这个时候文件还没有读取完全&#xff0c;所以获取到的文件大小是不完全的。…

乱序执行的原理——减轻数据灾难的影响

文/Hisa Ando 处理器基本上会按照程序中书写的机器指令的顺序执行。按照书写顺序执行称为按序执行(In-Order )。按照书写顺序执行时&#xff0c;如果从内存读取数据的加载指令、除法运算指令等延迟(等待结果的时间)较长的指令后面紧跟着使用该指令结果的指令&#xff0c;就会陷…

倍福 ton_b%C3%A9ton野蛮或野蛮

倍福 ton Brutalism style mainly has emphasis on materials, textures and construction, producing highly expressive forms. Popular in the 1960s and 1970s brutalism originated post–World War II when the design of low-cost housing and government buildings wer…

每周分享第 55 期

这里记录过去一周&#xff0c;我看到的值得分享的东西&#xff0c;每周五发布。 欢迎投稿&#xff0c;或推荐你自己的项目&#xff0c;请前往 GitHub 的 ruanyf/weekly 提交 issue。 (题图&#xff1a;昆山火车站&#xff0c;苏州&#xff0c;2018) 关于 996 工作制&#xff0c…

每周分享第 34 期

这里记录过去一周&#xff0c;我看到的值得分享的东西&#xff0c;每周五发布。 欢迎投稿&#xff0c;或推荐你自己的项目&#xff0c;请前往 GitHub 的 ruanyf/weekly 提交 issue。 英国有一家叫做 BioTeq 的创业公司&#xff0c;主营业务是人体芯片&#xff0c;也就是在人的体…

OpenStack 环境配置

OpenStack 环境配置 虚拟机资源信息 1、控制节点ct CPU&#xff1a;双核双线程-CPU虚拟化开启 内存&#xff1a;8G 硬盘&#xff1a;300G 双网卡&#xff1a;VM1-&#xff08;局域网&#xff09;192.168.100.20 NAT-192.168.80.20 操作系统&#xff1a;Centos 7.6&#xff0…

那一年,我们在巴塞罗那找到的「ONES 图腾」

临近2021年岁末&#xff0c;「圣诞之星」被悬挂到圣家族大教堂第二高塔「圣母塔」之上&#xff0c;这意味着大教堂进入了最后的施工阶段。 圣家族大教堂&#xff08;简称「圣家堂」&#xff09;被称为世界上最著名的「烂尾楼」——从1882年开始修建&#xff0c;至今依然没有建成…

天正网络版修改服务器地址,修改天正网络版服务器地址

修改天正网络版服务器地址 内容精选 换一换 修改子网名称、DNS服务器地址等。当前在部分区域中,子网已从虚拟私有云中解耦,解耦后子网拥有独立入口。未解耦:在虚拟私有云详情页的“子网”页签,可对子网进行操作。本小节的操作步骤指导以此入口为例。已解耦:在进入“网络 &…

vba 怎么取得一个book中最右边的sheet名_在阴影中一心前进 | 安藤忠雄:艰难的日子里坚韧地活...

李乐贤&#xff1a;在我20岁的时候&#xff0c;对未来和专业充满了憧憬但又迷茫&#xff1b;安藤忠雄的讲座和书陪伴我度过了非常艰难的一段日子。在我们很多次想要放弃的时候&#xff0c;他人生中的求学实践经历为所有的年轻建筑师带来了启发和坚韧。很多时候 &#xff0c;我们…