Java如何優(yōu)雅的實(shí)現(xiàn)微信登錄注冊(cè)
引言
今天我們來聊一聊微信登錄注冊(cè)遇到的一些事兒。
在我們的業(yè)務(wù)系統(tǒng)中,一個(gè)用戶在系統(tǒng)中肯定會(huì)有一個(gè)唯一標(biāo)識(shí),并且這個(gè)唯一標(biāo)識(shí)一般是從系統(tǒng)外部獲取的,而不是系統(tǒng)自動(dòng)生成的,例如:手機(jī)號(hào)或者身份證。
我們?cè)谖⑿诺膱?chǎng)景下(微信公眾號(hào)H5或者小程序),對(duì)于用戶的唯一標(biāo)識(shí)一般都是手機(jī)號(hào)或者openid。在正常情況下,我們遇到的都是一個(gè)用戶只有一個(gè)微信號(hào),一個(gè)微信號(hào)綁定了一個(gè)手機(jī)號(hào),所以我們就認(rèn)為三者的關(guān)系如下:
但是,理想很豐滿,現(xiàn)實(shí)很骨感,我們遇到的情況肯定不會(huì)如此的簡單。
問題分析
當(dāng)一個(gè)系統(tǒng)運(yùn)行的足夠久,用戶量足夠多,那么你總會(huì)遇到各種奇奇怪怪的問題。
在上一節(jié),我們知道正常情況遇到的場(chǎng)景會(huì)比較簡單,用戶、微信號(hào)、手機(jī)號(hào)三者是1:1:1的關(guān)系,也就說三者可以等價(jià),我用其中一個(gè)信息,總是可以查詢出另外兩個(gè)的信息,例如:我可以用手機(jī)號(hào),查詢出用戶ID和微信openid。
所以,根據(jù)以上思路,我們很容易設(shè)計(jì)出用戶表:cus_info ,基本表結(jié)構(gòu)如下:
用戶ID | 微信openid | 用戶手機(jī)號(hào) | 邏輯刪除 | 其他字段 |
---|---|---|---|---|
id | openid | mobile | del_flg | ... |
但是當(dāng)遇到以下2個(gè)場(chǎng)景的時(shí)候,這個(gè)表結(jié)構(gòu)設(shè)計(jì)就無法滿足需求了。
一個(gè)用戶2個(gè)微信號(hào)有些用戶是擁有兩個(gè)微信號(hào),并且綁定同一個(gè)手機(jī)號(hào)(這個(gè)邏輯可以通過微信換綁手機(jī)號(hào)實(shí)現(xiàn))。
在這個(gè)場(chǎng)景下,一旦用戶換了個(gè)微信號(hào)登錄進(jìn)入系統(tǒng)的時(shí)候,根據(jù)微信openid進(jìn)行登錄,因?yàn)楸頂?shù)據(jù)找不到該openid,則走注冊(cè)流程;在注冊(cè)的時(shí)候,又根據(jù)手機(jī)號(hào)查詢用戶信息,發(fā)現(xiàn)用戶已經(jīng)存在,返回登錄流程,最終造成邏輯死循環(huán)。
一個(gè)用戶2個(gè)手機(jī)號(hào)另外還有一些用戶擁有2個(gè)手機(jī)號(hào),并且綁定同一個(gè)手機(jī)號(hào)(這個(gè)邏輯在用戶授權(quán)手機(jī)號(hào)的時(shí)候添加另外一個(gè)手機(jī)號(hào)實(shí)現(xiàn))。
在這個(gè)場(chǎng)景下,第一次用戶使用手機(jī)號(hào)A注冊(cè)并登錄,我們?cè)诤蠖私壎耸謾C(jī)號(hào)A和對(duì)應(yīng)的微信openid;第二次用戶使用手機(jī)號(hào)B注冊(cè)并登錄,這時(shí)候數(shù)據(jù)庫會(huì)有2條記錄,不同手機(jī)號(hào)相同的openid。這樣子會(huì)導(dǎo)致在某些場(chǎng)景下(例如支付回調(diào)),根據(jù)openid獲取用戶信息的時(shí)候,找到2個(gè)用戶,從而導(dǎo)致業(yè)務(wù)異常。
解決思路
以上2個(gè)問題,在不同的業(yè)務(wù)場(chǎng)景下,不同的人會(huì)有不同的解法。有以手機(jī)號(hào)作為用戶的唯一標(biāo)識(shí),有以微信openid作為用戶唯一標(biāo)識(shí)。在這里,我們提供以手機(jī)號(hào)作為用戶唯一標(biāo)識(shí)的解法。
在這里,我們認(rèn)為一個(gè)手機(jī)號(hào)就是一個(gè)用戶,一個(gè)用戶會(huì)有多個(gè)微信號(hào)。關(guān)系如下:
一個(gè)用戶2個(gè)微信號(hào)針對(duì)該問題,我們?cè)诘卿涀?cè)的時(shí)候,會(huì)通過邏輯控制,保證一個(gè)手機(jī)號(hào)只能找到一個(gè)微信openid。處理方式如下:
- 根據(jù)當(dāng)前的手機(jī)號(hào)查詢到所有的微信openid,做邏輯刪除處理
- 根據(jù)當(dāng)前的openid查詢到所有的手機(jī)號(hào),做邏輯刪除處理
- 根據(jù)當(dāng)前手機(jī)號(hào)和openid查詢是否存在記錄,如果不存在則新增,如果存在則邏輯刪除標(biāo)識(shí)重置為正常。
一個(gè)用戶2個(gè)手機(jī)號(hào)針對(duì)該問題,我們?cè)跇I(yè)務(wù)上做處理。因?yàn)槲覀冋J(rèn)為了一個(gè)手機(jī)號(hào)就是一個(gè)用戶,如果一個(gè)用戶擁有兩個(gè)手機(jī)號(hào),那么在我們系統(tǒng)上我們認(rèn)為是兩個(gè)用戶,他們的數(shù)據(jù)是相互獨(dú)立的。
另外在這個(gè)場(chǎng)景下,我們還需要提供一個(gè)手機(jī)號(hào)換綁的功能。這樣當(dāng)用戶有2個(gè)手機(jī)號(hào),也能給實(shí)現(xiàn)切換的需求。
方案實(shí)現(xiàn)
以上,相關(guān)解決思路我們有了。那么接下來就是設(shè)計(jì)和編碼。
根據(jù)以上,我們會(huì)設(shè)計(jì)如下2張表結(jié)構(gòu):
cus_info 用戶信息表
用戶ID | 用戶手機(jī)號(hào) | 邏輯刪除 | 其他字段 |
---|---|---|---|
id | mobile | del_flg | ... |
cus_wx_info 用戶和微信關(guān)聯(lián)表
ID | 用戶手機(jī)號(hào) | 微信appId | 微信openid | 開放平臺(tái)unionid | 邏輯刪除 | 其他字段 |
---|---|---|---|---|---|---|
id | mobile | app_id | openid | unionid | del_flg | ... |
這里多添加了一個(gè)app_id的字段和unionid的字段,是用于當(dāng)我們的業(yè)務(wù)涉及到多個(gè)入口,例如微信公眾號(hào)H5入口和微信小程序。
不同的用戶在微信公眾號(hào)H5和微信小程序產(chǎn)生的openid可能一樣也可能不一樣,所以我們需要通過app_id來區(qū)分
同時(shí)為了關(guān)聯(lián)在微信公眾號(hào)H5和微信小程序的用戶,我們會(huì)把微信公眾號(hào)和微信小程序綁定到同一個(gè)開放平臺(tái),這個(gè)時(shí)候會(huì)產(chǎn)生一個(gè)unionid,通過該標(biāo)識(shí)即可以找到微信公眾號(hào)的用戶,也可以找到微信小程序的用戶。
接著我們實(shí)現(xiàn)一個(gè)注冊(cè)方法。
@Service public class CsInfoServiceImpl implements CsInfoService { @Autowired private CsInfoRepository csInfoRepository; @Autowired private CsWxInfoRepository csWxInfoRepository; @Autowired private CsInfoConvert csInfoConvert; @Override @Transactional(rollbackFor = Throwable.class, timeout = 60) public CsWxInfoDTO register(CsInfoRegisterDTO param) { // 根據(jù)手機(jī)號(hào)查詢用戶信息 CsInfo info = csInfoRepository.infoByMobile(param.getMobile()); Long id = info == null ? 0 : info.getId(); // 如果用戶不存在,則創(chuàng)建 if(id == 0){ id = csInfoRepository.create(param.getMobile(), param.getRegisterSource().getCode()); } // 邏輯刪除當(dāng)前手機(jī)號(hào)綁定的openid // 邏輯刪除當(dāng)前openid綁定的手機(jī)號(hào) csWxInfoRepository.handleOpenidMobileUnique(param.getMobile(), param.getOpenid(), param.getAppId()); // 保證當(dāng)前手機(jī)號(hào)和openid在系統(tǒng)中1:1的關(guān)系 CsWxInfo wxInfo = csWxInfoRepository.infoByMobileOpenid(param.getMobile(), param.getOpenid(), param.getAppId()); if(wxInfo == null){ wxInfo = new CsWxInfo(); wxInfo.setAppId(param.getAppId()); wxInfo.setMobile(param.getMobile()); wxInfo.setOpenid(param.getOpenid()); wxInfo.setUnionid(param.getUnionid()); wxInfo.setAvatarUrl(param.getAvatarUrl()); wxInfo.setNickName(param.getNickName()); csWxInfoRepository.save(wxInfo); }else{ CsWxInfo model = new CsWxInfo(); model.setId(wxInfo.getId()); model.setDelFlg(Integer.valueOf(DelFlgEnum.NORMAL.getCode())); csWxInfoRepository.updateById(model); } CsWxInfoDTO result = csInfoConvert.getCsWxInfoDTO(wxInfo); result.setInfoId(id); return result; } }
其中handleOpenidMobileUnique方法對(duì)應(yīng)的SQL處理如下:
<update id="loginDelByOpenIdExcludeMobile"> update cs_wx_info set del_flg = 0 ,update_time = now() <where> del_flg = 1 <if test="appId != null"> and app_id = #{appId} </if> <if test="openid != null and openid != ''"> and openid = #{openid} </if> <if test="mobile != null and mobile != ''"> and mobile != #{mobile} </if> </where> </update> <update id="loginDelByMobileExcludeOpenid"> update cs_wx_info set del_flg = 0 ,update_time = now() <where> del_flg = 1 <if test="appId != null"> and app_id = #{appId} </if> <if test="mobile != null and mobile != ''"> and mobile = #{mobile} </if> <if test="openid != null and openid != ''"> and openid != #{openid} </if> </where> </update>
最后
至此,關(guān)于微信登錄注冊(cè)遇到的一些小問題,我們找到了一個(gè)相對(duì)比較好解決方案,你還不快實(shí)踐到你自己項(xiàng)目上去?
相關(guān)源碼地址:Anyin Cloud
到此這篇關(guān)于Java如何優(yōu)雅的實(shí)現(xiàn)微信登錄注冊(cè)的文章就介紹到這了,更多相關(guān)Java微信登錄注冊(cè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java+mysql用戶注冊(cè)登錄功能
- JAVA簡單實(shí)現(xiàn)MD5注冊(cè)登錄加密實(shí)例代碼
- Java簡易登錄注冊(cè)小程序
- JavaWeb實(shí)現(xiàn)用戶登錄與注冊(cè)功能
- JavaWeb實(shí)現(xiàn)用戶登錄與注冊(cè)功能(服務(wù)器)
- JavaWeb實(shí)戰(zhàn)之用Servlet+JDBC實(shí)現(xiàn)用戶登錄與注冊(cè)
- 基于IO版的用戶登錄注冊(cè)實(shí)例(Java)
- Java實(shí)現(xiàn)多用戶注冊(cè)登錄的幸運(yùn)抽獎(jiǎng)
- javaweb實(shí)現(xiàn)注冊(cè)登錄頁面
- Java實(shí)現(xiàn)登錄與注冊(cè)頁面
相關(guān)文章
教你使用Java獲取當(dāng)前時(shí)間戳的詳細(xì)代碼
這篇文章主要介紹了如何使用Java獲取當(dāng)前時(shí)間戳,通過兩個(gè)java示例,向大家展示如何獲取java中的當(dāng)前時(shí)間戳,文本通過示例代碼給大家展示了java獲取當(dāng)前時(shí)間戳的方法,需要的朋友可以參考下2022-01-01SpringBoot中利用AOP和攔截器實(shí)現(xiàn)自定義注解
本文將通過攔截器+AOP實(shí)現(xiàn)自定義注解,在這里攔截器充當(dāng)在指定注解處要執(zhí)行的方法,aop負(fù)責(zé)將攔截器的方法和要注解生效的地方做一個(gè)織入,感興趣的可以嘗試一下2022-06-06java實(shí)現(xiàn)數(shù)據(jù)庫的數(shù)據(jù)寫入到txt的方法
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)數(shù)據(jù)庫的數(shù)據(jù)寫入到txt的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Java實(shí)現(xiàn)在Word中嵌入多媒體(視頻、音頻)文件
這篇文章主要介紹了Java如何實(shí)現(xiàn)在Word中嵌入多媒體(視頻、音頻)文件,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)java有一定的幫助,感興趣的同學(xué)可以了解一下2021-12-12SpringMVC請(qǐng)求/響應(yīng)亂碼問題解決方案解析
這篇文章主要介紹了SpringMVC請(qǐng)求/響應(yīng)亂碼問題解決方案解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12