java集成微信扫码登录

article/2025/9/13 2:43:59

具体流程可以看微信官网的扫码登录文档
地址:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN

一、 前期准备
1、注册
微信开放平台:https://open.weixin.qq.com
2、邮箱激活
3、完善开发者资料
4、开发者资质认证
准备营业执照,1-2个工作日审批、300元
5、创建网站应用
提交审核,7个工作日审批
6、内网穿透
ngrok的使用

二、扫码登录流程
1、首先准备工作

网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。

在进行微信OAuth2.在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,目前只支持企业不支持个人

申请微信登录且通过审核后,可开始接入流程。就是你的网站要想实现微信扫码登陆功能首先要在微信备案通过,它才会给你个AppID和AppSecret。

2、微信登录开发流程:
申请微信接入
生成登录二维码
用户扫码并授权
调用回调方法
通过code去获取用户信息带到页面展示

1. 申请微信接入:
先提醒下各位:申请微信接入很麻烦,需公司营业执照,个人好像暂时不能申请成功。

先去到 微信开放平台 https://open.weixin.qq.com
在这里插入图片描述

申请一个网站应用 (要审核通过之后才能用)在这里插入图片描述
appid和appSecret就是调用微信接口的凭证
授权回调域:调用微信接口生成的二维码地址,用户扫码并授权后,会重定向到回调地址
因为在本地localhost进行测试,如果回调地址为localhost第三方微信将无法进行跳转。原因是外网访问不到本地,怎么办?解决办法:那就使用 ngrok 内网穿透把本地项目服务映射到公网,所以在测试时填写的回调地址是内网穿透时的访问域名
如果不知道内网穿透的小伙伴,建议先看看 Sunny-Ngrok 内网穿透的使用

在这里插入图片描述
三、项目环境搭建:
1、导入jar包,这里请求微信地址时需要用到httpclient所以要导入httpclient的依赖,和json依赖用于解析数据。其他依赖根据项目需求自行引入

<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId>
</dependency>

2、配置文件

网站应用申请成功后会得到appid和appSecret
wx.open.app_id=
wx.open.app_secret=
//回调地址
wx.open.redirect_url=

3、HttpClient工具类

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;public class HttpClientUtils {public static final int connTimeout=10000;public static final int readTimeout=10000;public static final String charset="UTF-8";private static HttpClient client = null;static {PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();cm.setMaxTotal(128);cm.setDefaultMaxPerRoute(128);client = HttpClients.custom().setConnectionManager(cm).build();}public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception{return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);}public static String postParameters(String url, String parameterStr,String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception{return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);}public static String postParameters(String url, Map<String, String> params) throws ConnectTimeoutException,SocketTimeoutException, Exception {return postForm(url, params, null, connTimeout, readTimeout);}public static String postParameters(String url, Map<String, String> params, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,SocketTimeoutException, Exception {return postForm(url, params, null, connTimeout, readTimeout);}public static String get(String url) throws Exception {return get(url, charset, null, null);}public static String get(String url, String charset) throws Exception {return get(url, charset, connTimeout, readTimeout);}/*** 发送一个 Post 请求, 使用指定的字符集编码.** @param url* @param body RequestBody* @param mimeType 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3* @param charset 编码* @param connTimeout 建立链接超时时间,毫秒.* @param readTimeout 响应超时时间,毫秒.* @return ResponseBody, 使用指定的字符集编码.* @throws ConnectTimeoutException 建立链接超时异常* @throws SocketTimeoutException  响应超时* @throws Exception*/public static String post(String url, String body, String mimeType,String charset, Integer connTimeout, Integer readTimeout)throws ConnectTimeoutException, SocketTimeoutException, Exception {HttpClient client = null;HttpPost post = new HttpPost(url);String result = "";try {if (StringUtils.isNotBlank(body)) {HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));post.setEntity(entity);}// 设置参数Builder customReqConf = RequestConfig.custom();if (connTimeout != null) {customReqConf.setConnectTimeout(connTimeout);}if (readTimeout != null) {customReqConf.setSocketTimeout(readTimeout);}post.setConfig(customReqConf.build());HttpResponse res;if (url.startsWith("https")) {// 执行 Https 请求.client = createSSLInsecureClient();res = client.execute(post);} else {// 执行 Http 请求.client = HttpClientUtils.client;res = client.execute(post);}result = IOUtils.toString(res.getEntity().getContent(), charset);} finally {post.releaseConnection();if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {((CloseableHttpClient) client).close();}}return result;}/*** 提交form表单** @param url* @param params* @param connTimeout* @param readTimeout* @return* @throws ConnectTimeoutException* @throws SocketTimeoutException* @throws Exception*/public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,SocketTimeoutException, Exception {HttpClient client = null;HttpPost post = new HttpPost(url);try {if (params != null && !params.isEmpty()) {List<NameValuePair> formParams = new ArrayList<NameValuePair>();Set<Entry<String, String>> entrySet = params.entrySet();for (Entry<String, String> entry : entrySet) {formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));}UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);post.setEntity(entity);}if (headers != null && !headers.isEmpty()) {for (Entry<String, String> entry : headers.entrySet()) {post.addHeader(entry.getKey(), entry.getValue());}}// 设置参数Builder customReqConf = RequestConfig.custom();if (connTimeout != null) {customReqConf.setConnectTimeout(connTimeout);}if (readTimeout != null) {customReqConf.setSocketTimeout(readTimeout);}post.setConfig(customReqConf.build());HttpResponse res = null;if (url.startsWith("https")) {// 执行 Https 请求.client = createSSLInsecureClient();res = client.execute(post);} else {// 执行 Http 请求.client = HttpClientUtils.client;res = client.execute(post);}return IOUtils.toString(res.getEntity().getContent(), "UTF-8");} finally {post.releaseConnection();if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {((CloseableHttpClient) client).close();}}}/*** 发送一个 GET 请求*/public static String get(String url, String charset, Integer connTimeout,Integer readTimeout)throws ConnectTimeoutException,SocketTimeoutException, Exception {HttpClient client = null;HttpGet get = new HttpGet(url);String result = "";try {// 设置参数Builder customReqConf = RequestConfig.custom();if (connTimeout != null) {customReqConf.setConnectTimeout(connTimeout);}if (readTimeout != null) {customReqConf.setSocketTimeout(readTimeout);}get.setConfig(customReqConf.build());HttpResponse res = null;if (url.startsWith("https")) {// 执行 Https 请求.client = createSSLInsecureClient();res = client.execute(get);} else {// 执行 Http 请求.client = HttpClientUtils.client;res = client.execute(get);}result = IOUtils.toString(res.getEntity().getContent(), charset);} finally {get.releaseConnection();if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {((CloseableHttpClient) client).close();}}return result;}/*** 从 response 里获取 charset*/@SuppressWarnings("unused")private static String getCharsetFromResponse(HttpResponse ressponse) {// Content-Type:text/html; charset=GBKif (ressponse.getEntity() != null  && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {String contentType = ressponse.getEntity().getContentType().getValue();if (contentType.contains("charset=")) {return contentType.substring(contentType.indexOf("charset=") + 8);}}return null;}/*** 创建 SSL连接* @return* @throws GeneralSecurityException*/private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {try {SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {return true;}}).build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {@Overridepublic boolean verify(String arg0, SSLSession arg1) {return true;}@Overridepublic void verify(String host, SSLSocket ssl)throws IOException {}@Overridepublic void verify(String host, X509Certificate cert)throws SSLException {}@Overridepublic void verify(String host, String[] cns,String[] subjectAlts) throws SSLException {}});return HttpClients.custom().setSSLSocketFactory(sslsf).build();} catch (GeneralSecurityException e) {throw e;}}
}

4、后端controller接口

@Controller
@RequestMapping("/api/ucenter/wx")
public class WeixinApiController {@Autowiredprivate UserInfoService userInfoService;//微信扫描后回调的方法@GetMapping("callback")public String callback(String code,String state) {//1、获取到临时票据 codeSystem.out.println("code"+code);//2、拿着code和微信id和秘钥,请求微信固定地址StringBuffer baseAccessTokenUrl = new StringBuffer().append("https://api.weixin.qq.com/sns/oauth2/access_token").append("?appid=%s").append("&secret=%s").append("&code=%s").append("&grant_type=authorization_code");String accessTokenUrl = String.format(baseAccessTokenUrl.toString(),ConstantPropertiesUtil.WX_OPEN_APP_ID,ConstantPropertiesUtil.WX_OPEN_APP_SECRET,code);//使用httpclient请求这个地址try {String accesstokenInfo = HttpClientUtils.get(accessTokenUrl);System.out.println("accesstokenInfo:"+accesstokenInfo);//从返回字符串获取两个值 openid 和 access_tokenJSONObject jsonObject = JSONObject.parseObject(accesstokenInfo);String access_token = jsonObject.getString("access_token");String openid = jsonObject.getString("openid");//判断数据库是否存在微信扫码记录//根据openid判断UserInfo userInfo = userInfoService.selectWxInfoOpenId(openid);if (null == userInfo) {//拿着openid 和 access_token 请求微信地址,得到扫码人信息String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +"?access_token=%s" +"&openid=%s";String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);String resultInfo = HttpClientUtils.get(userInfoUrl);System.out.println("resultInfo"+resultInfo);JSONObject resultUserInfo = JSONObject.parseObject(resultInfo);//获取昵称String nickname = resultUserInfo.getString("nickname");//获取头像String headimgurl = resultUserInfo.getString("headimgurl");userInfo = new UserInfo();userInfo.setNickName(nickname);userInfo.setOpenid(openid);userInfo.setStatus(1);userInfoService.save(userInfo);}//返回mapMap<String,String> map = new HashMap<>();String name = userInfo.getName();if (StringUtils.isEmpty(name)) {name = userInfo.getNickName();}if (StringUtils.isEmpty(name)) {name = userInfo.getPhone();}map.put("name",name);//前端判断,如果openid不为空,说明没有绑定手机号,进行绑定,为空说明已经绑定,不再绑定if (StringUtils.isEmpty(userInfo.getPhone())) {map.put("openid",userInfo.getOpenid());} else {map.put("openid","");}String token = JwtHelper.createToken(userInfo.getId(), name);map.put("token",token);//注意我这里跳转的是一个前端的页面带着用户的信息return "redirect:"+ ConstantPropertiesUtil.YYGH_BASE_URL + "/weixin/callback?token="+map.get("token")+"&openid="+map.get("openid")+"&name="+URLEncoder.encode(map.get("name"),"utf-8");} catch (Exception e) {e.printStackTrace();return null;}}//1、生成微信扫描二维码//生成二维码需要的参数@ResponseBody@GetMapping("/getLoginParam")public Result getLoginParam() {try {Map<String,Object> map = new HashMap<>();String wxOpenRedirectUrl = ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL;String wxRedirectUri = URLEncoder.encode(wxOpenRedirectUrl, "utf-8");map.put("appid", ConstantPropertiesUtil.WX_OPEN_APP_ID);map.put("scope","snsapi_login");map.put("redirect_uri",wxRedirectUri);map.put("state",System.currentTimeMillis());return Result.ok(map);} catch (UnsupportedEncodingException e) {e.printStackTrace();return null;}}}

前台vue代码

<div><div id="weixinLogin"></div>
</div>
//点击事件
weixinLogin() {this.dialogAtrr.showLoginType = 'weixin'//初始化微信相关参数//调用后台controller的getLoginParam方法weixinApi.getLoginParam().then((result) => {var obj = new WxLogin({self_redirect:true,id: 'weixinLogin', // 需要显示的容器idappid: result.data.appid, // 公众号appid wx*******scope: result.data.scope, // 网页默认即可redirect_uri: result.data.redirect_uri, // 授权成功后回调的urlstate: result.data.state, // 可设置为简单的随机数加session用来校验style: 'black', // 提供"black"、"white"可选。二维码的样式href: '' // 外部css文件url,需要https})})
},

以上代码仅供参考,可根据自己的实际业务更改


http://chatgpt.dhexx.cn/article/LNQHPqj6.shtml

相关文章

企业微信扫码登录

企业微信扫码登录步骤&#xff1a; 1.首先在要放置二维码的页面提供一个盒子用于防止生成的二维码 2.在当前页面将企业微信提供的js进行引入 3.调用提供的方法实例&#xff0c; 4.要获得扫码成功之后的code和state值&#xff0c;调用服务&#xff0c;就能查到当前用户的token&…

扫描微信二维码实现快速登录

一、什么是二维码 二维码又称二维条码&#xff0c;常见的二维码为QR Code&#xff0c;QR全称Quick Response&#xff0c;是一个近几年来移动设备上超流行的一种编码方式&#xff0c;它比传统的Bar Code条形码能存更多的信息&#xff0c;也能表示更多的数据类型。二维条码/二维码…

个人网站如何使用微信扫一扫登录---SpringBoot项目

文章目录 前言一、码上登录是什么&#xff1f;二、使用步骤1.登录前准备&#xff0c;获取SecretKey2.前端发起登录请求3.后端调用“码上登录”服务器3.1. 配置文件3.2. 发起请求3.3.返回的参数 4.前端显示二维码5、扫一扫登录6 、登录成功后接收用户信息7、通知前端登录成功并返…

微信扫码登录是如何实现的?

网页版微信刚推出时&#xff0c;无数人被它的登录方式惊艳了一下&#xff0c;不需要输入用户名密码&#xff0c;打开手机微信扫一扫&#xff0c;便自动登录。从原理上讲&#xff0c;二维码只能是一段文本的编码&#xff0c;如何用它实现快捷登录的呢&#xff1f; 打开网页版微…

Python 最强 IDE 详细使用指南!

本文经机器之心&#xff08;微信公众号&#xff1a;almosthuman2014&#xff09;授权转载&#xff0c;禁止二次转载 选自RealPython&#xff0c;作者&#xff1a;Jahongir Rahmonov 机器之心编译&#xff0c;参与&#xff1a;魔王 PyCharm 是一种 Python IDE&#xff0c;可以帮…

用 Python 给全球女神颜值排个序

点击上方“码农突围”&#xff0c;马上关注&#xff0c;每天上午8:50准时推送 这里是码农充电第一站&#xff0c;回复“666”&#xff0c;获取一份专属大礼包 真爱&#xff0c;请设置“星标”或点个“在看” 作者 | 数据森麟 来源 | 数据森麟&#xff08;ID: shujusenlin&#…

官宣:Python 3.8正式发布!来看看有哪些新功能

点击上方“码农突围”&#xff0c;马上关注&#xff0c;每天上午8:50准时推送 这里是码农充电第一站&#xff0c;回复“666”&#xff0c;获取一份专属大礼包 真爱&#xff0c;请设置“星标”或点个“在看” 来源&#xff1a;.python.org 编辑&#xff1a;肖琴&#xff0c;转自…

人工智能可以产生自主意识吗?

作者&#xff1a;人民邮电出版社 链接&#xff1a;https://www.zhihu.com/question/372639666/answer/1343242547 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 「既然人类对自己存在的认知来源于“感知”和“记忆…

CET6 历年真题原词复现 核心词汇 做题技巧 听力拿分技巧汇总(这篇文章是英语四六级考试最后的版本,谢谢大家一路陪伴)

话不多说直接进入 这次是最后一次 也是最用心的一次关于CET6的整理&#xff0c;CET4很简单&#xff0c;我认为没有必要去整理或者帮助各位同学&#xff0c;cet4只是cet6的前置任务罢了&#xff0c;但要是你说你考完cet4就收手了&#xff0c;不考了&#xff0c;不考6级了。我只…

这一款神器,助你秒级定位线上问题!

点击上方“Github中文社区”&#xff0c;关注 触达Github项目来源&#xff1a;https://my.oschina.net/leejun2005/blog/1524687 背景 经常做后端服务开发的同学&#xff0c;或多或少都遇到过 CPU 负载特别高的问题。 尤其是在周末或大半夜&#xff0c;突然群里有人反馈线上机器…

前端工程师高手说说CSS学习中的瓶颈

一、何为瓶颈&#xff1f; “瓶颈”指瓶子的颈部&#xff0c;相对狭窄。这是很传神的一个词&#xff0c;因为狭窄&#xff0c;因此难以突破&#xff1b;但是&#xff0c;一旦突破了&#xff0c;就是广阔天空&#xff08;偌大瓶身&#xff09;&#xff01; 小弟不才&#xff0…

线上服务 CPU 100%?一键定位 so easy!

转自&#xff1a;大数据之路&#xff0c; 链接&#xff1a;my.oschina.net/leejun2005/blog/1524687 背景 经常做后端服务开发的同学&#xff0c;或多或少都遇到过 CPU 负载特别高的问题。 尤其是在周末或大半夜&#xff0c;突然群里有人反馈线上机器负载特别高&#xff0c;不熟…

线上服务 CPU 100% ?一键定位 so easy!

来源&#xff1a;my.oschina.net/leejun2005/blog/1524687 背景 经常做后端服务开发的同学&#xff0c;或多或少都遇到过 CPU 负载特别高的问题。 尤其是在周末或大半夜&#xff0c;突然群里有人反馈线上机器负载特别高&#xff0c;不熟悉定位流程和思路的同学可能登上服务器一…

牛逼的故障诊断工具!秒级定位线上问题

背景 经常做后端服务维护或开发的同学&#xff0c;或多或少都遇到过CPU 负载特别高的问题。尤其是在周末或大半夜&#xff08;有没有同感&#xff0c;平时不出问题&#xff0c;一到休息或下班时间频繁出故障&#xff0c;有的文末点个在看示意一下&#xff09;&#xff0c;突然群…

推荐一款神器,助你秒级定位线上问题!

来源&#xff1a;my.oschina.net/leejun2005/blog/1524687 背景 经常做后端服务开发的同学&#xff0c;或多或少都遇到过 CPU 负载特别高的问题。 尤其是在周末或大半夜&#xff0c;突然群里有人反馈线上机器负载特别高&#xff0c;不熟悉定位流程和思路的同学可能登上服务器一…

最牛逼的故障诊断工具!秒级定位线上问题

点击下方公众号「关注」和「星标」 回复“1024”获取独家整理的学习资料&#xff01; 背景 经常做后端服务维护或开发的同学&#xff0c;或多或少都遇到过CPU 负载特别高的问题。尤其是在周末或大半夜&#xff08;有没有同感&#xff0c;平时不出问题&#xff0c;一到休息或下班…

WheelView地区选择三级联动详解

1. 效果 最近需要做一个地区选择的功能&#xff0c;但是在网上和github上找了很久都没找到满意的&#xff0c;然后朋友推荐了一个给我&#xff0c;我花了点时间把代码大致看懂并改成我想要的&#xff0c;并写上我的理解。效果如图&#xff1a; 2. 注意 a. 首先我们要明白…

如何用美剧真正提升你的英语水平————转自厦大口译的博客

看到很多童鞋讨论有关美剧学习英语到底有没有用&#xff0c;以及用哪部美剧练习&#xff0c;我在这里想说这只是一个参考&#xff0c;世界上没有绝对的事情&#xff0c;究竟有没有用看个人 1. 不是所有的美剧都适合学英语 如果喜欢看如《24小时》这样的动作片, 那你基本会讲一口…

美剧命名规则

缘起 最近工作需要研究了下ffmpeg这个工具.在查资料的时候意外发现美剧制作组发布的美剧的名字是有规则的. 美剧命名规则 剧名.S季数E集数.集名(可以不标).发布年代(可以不标).分辨率(可以不标).信号采集源.音频编码(默认的MP3可以不标).视频编码-制作组 下面举例子说一下吧. 例…

EOS 智能合约

1. EOS智能合约的介绍 1.1. 所需背景知识 C / C 经验 基于EOS.IO的区块链使用Web Assembly(WASM)执行开发者提供的应用代码。WASM是一个已崭露头角的web标准&#xff0c;受到Google, Microsoft, Apple及其他大公司的广泛支持。目前为止&#xff0c;最成熟的用于构建应用及WA…