SpringBoot + UniApp對(duì)接微信OAuth2登錄的全過(guò)程
本文將手把手帶你打通SpringBoot后端與UniApp前端的微信登錄全流程,并分享那些官方文檔沒(méi)寫(xiě)的實(shí)戰(zhàn)經(jīng)驗(yàn)。
前期準(zhǔn)備
在開(kāi)始編碼前,這些準(zhǔn)備工作必須到位:
- 微信開(kāi)放平臺(tái)賬號(hào)(個(gè)人/企業(yè)均可)
- 已審核通過(guò)的移動(dòng)應(yīng)用(審核通常需要1-3個(gè)工作日)
- 獲取AppID和AppSecret(這是微信登錄的“身份證”)
特別注意:微信登錄權(quán)限需要單獨(dú)申請(qǐng),通過(guò)審核后才能正常使用!
前端實(shí)戰(zhàn):Uniapp一鍵登錄按鈕
關(guān)鍵代碼實(shí)現(xiàn)
<template>
<view class="container">
<button class="wechat-btn" open-type="getUserInfo" @click="handleWechatLogin">
<uni-icons type="weixin" color="#fff" size="30"></uni-icons>
微信一鍵登錄
</button>
</view>
</template>
<script>
export default {
methods: {
async handleWechatLogin() {
try {
// 獲取微信授權(quán)碼
const loginRes = await uni.login({
provider: "weixin",
onlyAuthorize: true, // 關(guān)鍵參數(shù):僅授權(quán)不獲取用戶(hù)信息
});
// 調(diào)用后端登錄接口
const res = await uni.request({
url: "你的后端地址/weChatLogin", // 記得換成你的實(shí)際地址!
method: 'POST',
data: { socialCode: loginRes.code }
});
if (res.data.token) {
// 登錄成功處理
uni.showToast({ title: '登錄成功!', icon: 'success' });
} else {
// 需要綁定手機(jī)號(hào)
this.goToBindPhone();
}
} catch (error) {
uni.showToast({ title: '登錄失敗,請(qǐng)重試', icon: 'none' });
}
}
}
}
</script>
核心要點(diǎn):
- onlyAuthorize: true 參數(shù)確保符合微信規(guī)范
- 異常捕獲必不可少,網(wǎng)絡(luò)請(qǐng)求總有意外
- token為空時(shí)的處理:跳轉(zhuǎn)手機(jī)號(hào)綁定頁(yè)面
后端開(kāi)發(fā):Springboot接收與處理
后端的核心使命:安全驗(yàn)證 + 用戶(hù)管理 + 令牌發(fā)放。
- 控制器層
@Tag(name = "微信登錄")
@RestController
public class SysLoginController {
@Autowired
private SysLoginService loginService;
@Operation(summary = "微信授權(quán)登錄")
@PostMapping("/weChatLogin")
public AjaxResult weChatLogin(@RequestBody LoginBodyWeChat loginBody) {
// 三步走:授權(quán) -> 獲取用戶(hù)信息 -> 生成令牌
Map<String, String> authResult = weiXinApiService.authorize(loginBody.getSocialCode());
Map<String, String> userInfo = weiXinApiService.getUserInfo(
authResult.get("access_token"),
authResult.get("openid")
);
String token = loginService.weChatLogin(userInfo);
// 返回令牌給前端
return AjaxResult.success()
.put(Constants.TOKEN, token)
.put("unionid", userInfo.get("unionid"));
}
}
- 微信API服務(wù)
@Component
public class WeiXinApiService {
public Map<String, String> authorize(String socialCode) {
String url = String.format(
"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
appid, secret, socialCode
);
String response = HttpUtil.get(url);
JSONObject jsonObject = JSONUtil.parseObj(response);
// 錯(cuò)誤處理絕不能少!
if (jsonObject.containsKey("errcode")) {
throw new ServiceException("微信登錄失敗:" + jsonObject.get("errmsg"));
}
// 返回access_token和openid
return Map.of(
"access_token", jsonObject.getStr("access_token"),
"openid", jsonObject.getStr("openid")
);
}
public Map<String, String> getUserInfo(String accessToken, String openId) {
String url = String.format("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s",
accessToken, openId);
String response = HttpUtil.get(url);
JSONObject jsonObject = JSONUtil.parseObj(response);
if (jsonObject.containsKey("errcode")) {
throw new ServiceException("獲取微信用戶(hù)信息失?。? + jsonObject.get("errmsg"));
}
Map<String, String> result = new HashMap<>();
result.put("nickname", jsonObject.getStr("nickname"));
result.put("sex", jsonObject.getStr("sex"));
result.put("headimgurl", jsonObject.getStr("headimgurl"));
result.put("unionid", jsonObject.getStr("unionid"));
result.put("openid", jsonObject.getStr("openid"));
return result;
}
}
- 業(yè)務(wù)邏輯層:用戶(hù)存在就登錄,不存在就創(chuàng)建
public String weChatLogin(Map<String, String> userInfo) {
// 用unionid查詢(xún)用戶(hù)
SysUser user = userService.selectUserByOpenId(userInfo.get("unionid"));//這里可以根據(jù)自己的業(yè)務(wù)框架來(lái)處理
if (user == null) {
// 新用戶(hù):自動(dòng)注冊(cè)
user = createNewUser(userInfo);
userService.insertUser(user);//這里可以根據(jù)自己的業(yè)務(wù)框架來(lái)處理
}
// 未綁定手機(jī)號(hào)?引導(dǎo)綁定
if (StringUtils.isBlank(user.getPhonenumber())) {
return ""; // 前端根據(jù)空token跳轉(zhuǎn)綁定頁(yè)面
}
// 執(zhí)行登錄,生成token
return performLogin(user.getPhonenumber());//這里可以根據(jù)自己的業(yè)務(wù)框架來(lái)處理
}
核心概念解析:openid vs unionid
很多開(kāi)發(fā)者在這里踩坑,其實(shí)很簡(jiǎn)單:
- openid:用戶(hù)在單個(gè)應(yīng)用內(nèi)的身份證(同一個(gè)用戶(hù)在不同應(yīng)用openid不同)
- unionid:用戶(hù)在微信開(kāi)放平臺(tái)體系的身份證(同一用戶(hù)在不同應(yīng)用unionid相同)
簡(jiǎn)單理解:openid是部門(mén)工號(hào),unionid是公司工號(hào)
手機(jī)號(hào)綁定
重要提醒:微信官方已限制獲取用戶(hù)手機(jī)號(hào)!我們的解決方案:
- 自動(dòng)檢測(cè):用戶(hù)首次微信登錄后,檢測(cè)是否已綁定手機(jī)
- 友好引導(dǎo):token為空時(shí),前端跳轉(zhuǎn)到綁定頁(yè)面
- 簡(jiǎn)化流程:只需驗(yàn)證碼即可完成綁定
參考代碼:
@PostMapping("/bindPhoneNumber")
public AjaxResult bindPhoneNumber(@RequestBody LoginBodyBindPhone loginBody) {
// 驗(yàn)證碼校驗(yàn) + 綁定手機(jī)號(hào)
String token = loginService.bindPhoneNumber(loginBody);
return AjaxResult.success().put(Constants.TOKEN, token);
}
@Data
public class LoginBodyBindPhone {
@Schema(description = "手機(jī)號(hào)")
private String phonenumber;
@Schema(description = "微信用戶(hù)統(tǒng)一標(biāo)識(shí)")
private String unionid;
@Schema(description = "驗(yàn)證碼")
private String code;
}
微信登錄對(duì)接就像搭積木——步驟固定,但細(xì)節(jié)決定成敗。掌握核心流程(獲取code → 換取token → 獲取用戶(hù)信息 → 業(yè)務(wù)處理),你就能應(yīng)對(duì)各種場(chǎng)景。
總結(jié)
到此這篇關(guān)于SpringBoot + UniApp對(duì)接微信OAuth2登錄的文章就介紹到這了,更多相關(guān)SpringBoot UniApp對(duì)接微信OAuth2登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SprintBoot深入淺出講解場(chǎng)景啟動(dòng)器Starter
本篇文章將和大家分享一下 Spring Boot 框架中的 Starters 場(chǎng)景啟動(dòng)器的內(nèi)容,關(guān)于 Starters 具體是用來(lái)做什么的,以及在開(kāi)發(fā) Spring Boot項(xiàng)目前,要如何自定義一個(gè) Starters 場(chǎng)景啟動(dòng)器2022-06-06
Mybatis和Mybatis-Plus時(shí)間范圍查詢(xún)方式
這篇文章主要介紹了Mybatis和Mybatis-Plus時(shí)間范圍查詢(xún)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
springcloud?nacos動(dòng)態(tài)線(xiàn)程池Dynamic?tp配置接入實(shí)戰(zhàn)詳解
這篇文章主要為大家介紹了springcloud?nacos動(dòng)態(tài)線(xiàn)程池Dynamic?tp配置接入實(shí)戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Java并發(fā)編程volatile關(guān)鍵字的作用
這篇文章主要介紹了Java并發(fā)編程volatile關(guān)鍵字的作用,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07
java實(shí)現(xiàn)后臺(tái)圖片跨域上傳功能
這篇文章主要給大家介紹了關(guān)于java實(shí)現(xiàn)后臺(tái)圖片跨域上傳功能的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
關(guān)于SpringGateway調(diào)用服務(wù) 接受不到參數(shù)問(wèn)題
這篇文章主要介紹了關(guān)于SpringGateway調(diào)用服務(wù)接受不到參數(shù)問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12

