0.简介
实现在微信小程序的点击一键登录的按钮获取当前微信用户唯一且统一的openId,也可以获取手机号和用户名,本来想和网页一起做一个二维码绑定,发现要收300的注册费用。
核心就是小程序前端调用自带的login向后端发送一个带code给后端,后端将这个code和小程序校验的数据给微信,微信返回该用户的唯一openid。
wx.login() 是微信小程序官方提供的用户登录凭证获取接口,其核心功能是:
当你在小程序中调用 wx.login() 时,微信客户端会向微信官方服务器发起请求,微信服务器验证请求合法性后,生成一个临时的、唯一的登录凭证,并通过 success 回调函数的 res 参数返回给小程序前端,这个凭证就是 res.code。

1.小程序
创建一个简单的登录按钮和显示界面
index.wxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <view class="container"> <button class="login-btn" bindtap="getOpenid" open-type="getUserInfo" withCredentials="true" > 一键登录 </button> <view class="result" wx:if="{{openid}}"> 登录成功!你的openid是:{{openid}} </view> </view>
|
index.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| Page({ data: { openid: '', errorMsg: '' }, getOpenid() { wx.showLoading({ title: '登录中...', }) this.setData({ errorMsg: '' }) wx.login({ success: (res) => { console.log('wx.login成功,code:', res.code); if (res.code) { wx.request({ url: 'http://localhost:3000/api/login', method: 'POST', data: { code: res.code }, success: (response) => { wx.hideLoading() console.log('后端返回结果:', response.data); if (response.data.success && response.data.openid) { this.setData({ openid: response.data.openid }) wx.setStorageSync('openid', response.data.openid) wx.showToast({ title: '登录成功', icon: 'success' }) } else { const msg = response.data.message || '登录失败'; this.setData({ errorMsg: msg }); wx.showToast({ title: msg, icon: 'none', duration: 3000 }) } }, fail: (err) => { wx.hideLoading() const errMsg = '网络请求失败: ' + (err.errMsg || '未知错误'); this.setData({ errorMsg: errMsg }); wx.showToast({ title: errMsg, icon: 'none', duration: 3000 }) console.error('请求失败:', err) } }) } else { wx.hideLoading() const errMsg = '获取code失败: ' + res.errMsg; this.setData({ errorMsg: errMsg }); wx.showToast({ title: errMsg, icon: 'none', duration: 3000 }) console.error('登录失败!' + res.errMsg) } }, fail: (err) => { wx.hideLoading() const errMsg = '登录接口调用失败: ' + err.errMsg; this.setData({ errorMsg: errMsg }); wx.showToast({ title: errMsg, icon: 'none', duration: 3000 }) console.error('wx.login失败:', err) } }) } })
|
index.wxss
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| .container { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; background-color: #f5f5f5; } .login-btn { width: 80%; height: 48px; line-height: 48px; background-color: #07c160; color: white; border-radius: 24px; font-size: 16px; } .result { margin-top: 30px; padding: 20px; background-color: white; border-radius: 8px; word-break: break-all; max-width: 80%; }
|
2. SpringBoot后端
pom
加入json格式化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
|

RestTemplateConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package com.kangdd.wxlogin;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.client.RestTemplate;
import java.util.ArrayList; import java.util.List;
@Configuration public class RestTemplateConfig {
@Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> converters = new ArrayList<>();
MappingJackson2HttpMessageConverter jacksonConverter = null; for (HttpMessageConverter<?> converter : restTemplate.getMessageConverters()) { if (converter instanceof MappingJackson2HttpMessageConverter) { jacksonConverter = (MappingJackson2HttpMessageConverter) converter; break; } }
if (jacksonConverter != null) { List<MediaType> mediaTypes = new ArrayList<>(jacksonConverter.getSupportedMediaTypes()); mediaTypes.add(MediaType.TEXT_PLAIN); jacksonConverter.setSupportedMediaTypes(mediaTypes); converters.add(jacksonConverter); }
for (HttpMessageConverter<?> converter : restTemplate.getMessageConverters()) { if (!(converter instanceof MappingJackson2HttpMessageConverter)) { converters.add(converter); } }
restTemplate.setMessageConverters(converters); return restTemplate; } }
|
WechatLoginController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| package com.kangdd.wxlogin;
import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;
import java.util.HashMap; import java.util.Map;
@RestController public class WechatLoginController {
@Value("${wechat.appid}") private String appId;
@Value("${wechat.secret}") private String appSecret;
private final RestTemplate restTemplate;
public WechatLoginController(RestTemplate restTemplate) { this.restTemplate = restTemplate; }
@PostMapping("/api/login") public Map<String, Object> login(@RequestBody Map<String, String> request) { Map<String, Object> response = new HashMap<>(); System.out.println("接收到请求!code: " + request.get("code"));
String code = request.get("code"); if (code == null || code.isEmpty()) { response.put("success", false); response.put("message", "缺少code参数"); System.out.println("返回结果: " + response); return response; }
if (appId == null || appId.isEmpty() || appSecret == null || appSecret.isEmpty()) { response.put("success", false); response.put("message", "appid或secret未配置"); System.out.println("返回结果: " + response); return response; }
String url = String.format( "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", appId, appSecret, code ); System.out.println("请求微信接口URL: " + url);
try { Map<String, Object> result = restTemplate.getForObject(url, Map.class); System.out.println("微信接口返回: " + result);
if (result.containsKey("errcode")) { response.put("success", false); response.put("message", "微信接口返回错误: " + result.get("errmsg")); response.put("errcode", result.get("errcode")); System.out.println("返回结果: " + response); return response; }
response.put("success", true); response.put("openid", result.get("openid")); System.out.println("返回结果: " + response); return response; } catch (Exception e) { response.put("success", false); response.put("message", "请求微信接口失败: " + e.getMessage()); e.printStackTrace(); System.out.println("返回结果: " + response); return response; } } }
|
配置
1 2 3 4 5 6
| # 微信小程序配置 wechat.appid=### wechat.secret=###
server.port=4678
|
3.效果
