(1)小程序端調(diào)用 wx.login方法獲取用戶登錄憑證code,將code發(fā)送給小程序后臺服務(wù)器;服務(wù)器調(diào)用登錄憑證校驗接口(需要傳參appid+appsecret+code),進而換取用戶登錄態(tài)信息,包括用戶的唯一標識(openid)及本次登錄的會話密鑰(session_key)等,將這些信息存入緩存中。
(2)點擊button按鈕觸發(fā)獲取微信手機號彈框!
(3)綁定微信賬號,存在相應(yīng)手機號賬號時自動登錄;不存在時自動創(chuàng)建
1.App登錄,如果沒有綁定過,重新綁定,es_third_login插入一條記錄,并且要存unionId字段
2.APP登錄,如果之前綁定過,并且之前的es_third_login表沒有存unionId,則更新原數(shù)據(jù)存入unionId
3.小程序登錄,如果之前APP未授權(quán)登錄,沒有存入unionId,首次授權(quán)的時候,會在es_third_login插入一條記錄
4.小程序登錄,如果之前APP授權(quán)登錄過,存過unionId,那么就直接登錄,不會插入數(shù)據(jù)。
小程序和APP的unionId是相同的,小程序和APP微信聯(lián)合登錄,是通過unionId實現(xiàn)的。只要es_third_login表中同一個會員存在unionId就可以直接登錄。
App登錄: 調(diào)用 thirdLoginSubmit接口
小程序登錄: 調(diào)用thirdLoginSubmit接口
授權(quán)登錄:調(diào)用getUnionId接口獲取到UnionId調(diào)用thirdLoginSubmit接口;
thirdLoginSubmit登錄接口:
@Override
public BaseResponse<Map<String,Object>> thirdLoginSubmit(@RequestBody ThirdLoginInfoRequest request){
try {
if(request==null){
return MessageResponse.messageResponse("500","false","shop.requestParam.empty",null);
}
String deviceId=request.getDeviceId();
String pluginId=request.getPluginId();
String openId=request.getOpenId();
String appType=request.getAppType();
String unionId = request.getUnionId();
ThirdLogin thirdLogin = thirdLoginService.find(pluginId, openId);
ThirdLogin thirdLoginByUnionId = null;
if (thirdLogin == null){
thirdLoginByUnionId = thirdLoginService.findByUnionId(unionId);
}else{
thirdLoginByUnionId = thirdLogin;
}
HashMap<String, Object> retMap = new HashMap<String, Object>();
if(thirdLoginByUnionId!=null){
Member member = memberService.find(thirdLoginByUnionId.getMember());
Map<String, Object> result = registerMemberUtil.loginCheck(member, RegisterMemberUtil.Type.app, deviceId, appType, request.getAppVersion());
if("error".equals(result.get("type"))){
return MessageResponse.messageResponse("500", "false", result.get("message").toString(), null);
}else {
member=(Member)result.get("member");
}
//老賬戶unionId為空的,存入unionId
if(StringUtils.isBlank(thirdLoginByUnionId.getUnionId())){
thirdLoginByUnionId.setUnionId(request.getUnionId());
thirdLoginByUnionId.setLastModifiedDate(new Date());
thirdLoginService.update(thirdLoginByUnionId);
}
VipDto vipDto = new VipDto();
vipDto.setMessageId(UUID.randomUUID().toString().replace("-",""));
vipDto.setMemberId(member.getId());
vipDto.setAppType(member.getAppType());
vipDto.setDeviceId(member.getDeviceId());
vipDto.setMessageType(MessageType.login);
loginProducer.send(GsonUtil.toJson(vipDto));
ckToReceiptProducer.send(vipDto);
retMap.put("root", "login");
String appToken=JWTUtils.createAppToken(member.getId(),member.getPassword(),member.getDeviceId(),member.getAppLoginDate());
if(StringUtils.isNotEmpty(appToken) && appToken.startsWith("CharlesKeith ")){
RedisUtils.set(APP_TOKEN + member.getId(), appToken);
appToken=appToken.replaceFirst("CharlesKeith ","");
}
retMap.put(Constants.TOKEN_PARAMETER_NAME, appToken);
retMap.put(Constants.USER_ID, member.getId());
retMap.put(Constants.USERNAME, member.getUsername());
retMap.put(Constants.USERTYPE, member.getType().name());
retMap.put(Constants.REGISTER_LOCATION, member.getRegisterLocation());
retMap.put(Constants.MOBILE, member.getMobile());
retMap.put("valid",valid(member.getId()));
//添加購物車
addCart(request,member);
}else {
retMap.put("root", "bindOrRegister");
}
return BaseResponse.successResponse(retMap);
}catch (Exception e){
e.printStackTrace();
}
return BaseResponse.errorResponse();
}
首先從request當中獲取到unionId,pluginId,openId,先根據(jù)pluginId,和openId查詢第三方綁定信息,根據(jù)pluginId和openId查詢到的第三方綁定信息為空則根據(jù)unionId查詢第三方綁定信息
如果查詢到的第三方綁定信息為空則返回一個map,封裝"root":"bindOrRegister"信息給前端;
如果查詢到的第三方綁定信息不為空則:從第三方綁定信息中獲取到memberId查詢會員信息,對會員信息進行登錄校驗,校驗第三方登錄信息是否為空,為空的話把request中的unionid,修改時間跟新保存第三方信息.
異步調(diào)用登錄活動發(fā)劵,同步小票信息,封裝"root","login"到map中生成token信息,把token和會員信息封裝到map當中返回給前端,跟新購物車信息;
thirdBindOrRegister第三方登錄或注冊接口:
@Override
public BaseResponse<Map<String,Object>> thirdBindOrRegister(@RequestBody ThirdLoginInfoRequest request){
try {
if(request==null){
return MessageResponse.messageResponse("500","false","shop.parameter.notempty",null);
}
String mobile=request.getMobile();
String captcha=request.getCaptcha();
VerificationCodeRequest codeRequest=new VerificationCodeRequest();
codeRequest.setPhoneNumber(mobile);
VerificationCodeResponse codeResponse = null;
BaseResponse<VerificationCodeResponse> verificationCode = smsClient.getVerificationCode(codeRequest);
if(verificationCode!=null && "true".equals(verificationCode.getStatus())){
codeResponse=verificationCode.getResult();
}
if(codeResponse==null || !codeResponse.getVerifyCode().equals(captcha)){
return MessageResponse.messageResponse("500","false","shop.code.wrong",null);
}
String pluginId=request.getPluginId();
String deviceId=request.getDeviceId();
String openId=request.getOpenId();
String appType=request.getAppType();
String unionId = request.getUnionId();
//小程序不需要傳openId
if(!"wx".equals(appType)){
if(StringUtils.isBlank(pluginId) || StringUtils.isBlank(openId)){
return MessageResponse.messageResponse("500","false","shop.common.invalid",null);
}
}
Member member = memberService.findByMobile(mobile);
if(member!=null){
ThirdLogin thirdLogin = thirdLoginService.findByPluginId(pluginId, member.getId());
if(Validator.isNotNullOrEmpty(thirdLogin) && Validator.isNotNullOrEmpty(thirdLogin.getOpenId()) && thirdLogin.getOpenId().equals(openId)){
return MessageResponse.messageResponse("500","false","shop.thirdAccount.error",null);
}
Map<String, Object> checkResult = registerMemberUtil.loginCheck(member, RegisterMemberUtil.Type.app, appType, null,request.getAppVersion());
if(checkResult.get("type").equals("error")){
return MessageResponse.messageResponse("500","false",checkResult.get("message").toString(),null);
}else{
member=(Member) checkResult.get("member");
}
VipDto vipDto=new VipDto();
vipDto.setMessageId(UUID.randomUUID().toString().replace("-",""));
vipDto.setMemberId(member.getId());
vipDto.setDeviceId(member.getDeviceId());
vipDto.setAppType(member.getAppType());
vipDto.setMessageType(MessageType.login);
loginProducer.send(GsonUtil.toJson(vipDto));
ckToReceiptProducer.send(vipDto);
}else{
// 注冊操作(沒有密碼)
member=new Member();
member.setCreatedDate(new Date());
member.setLastModifiedDate(new Date());
member.setRegisterLocation(Constants.REGISTER_APP);
member.setMemberRank(memberRankService.findDefault().getId());
member.setAppLoginDate(new Date());
member.setMobile(mobile);
member.setDeviceId(deviceId);
member.setAppType(appType);
member.setType(MemberType.normal);
member.setPoint(Long.valueOf(0L));
member.setBalance(BigDecimal.ZERO);
member.setAmount(BigDecimal.ZERO);
member.setIsLocked(Boolean.valueOf(false));
member.setIsEnabled(Boolean.valueOf(true));
member.setLoginFailureCount(Integer.valueOf(0));
member.setAppVersion(request.getAppVersion());
Map<String,Object> checkResult = registerMemberUtil.registerCheck(member, RegisterMemberUtil.Type.app,null);
if(checkResult.get("type").equals("error")){
return MessageResponse.messageResponse("500","false",checkResult.get("message").toString(),null);
}else {
member = (Member) checkResult.get("member");
}
Member sourceMember = memberService.saveAndReturn(member);
VipDto vipDto=new VipDto();
vipDto.setMessageId(UUID.randomUUID().toString().replace("-",""));
vipDto.setMemberId(sourceMember.getId());
vipDto.setMessageType(MessageType.register);
ckToReceiptProducer.send(vipDto);
}
ThirdLogin thirdLogin=new ThirdLogin();
thirdLogin.setCreatedDate(new Date());
thirdLogin.setLastModifiedDate(new Date());
thirdLogin.setMember(member.getId());
thirdLogin.setOpenId(openId);
thirdLogin.setPluginId(pluginId);
thirdLogin.setUnionId(unionId);
thirdLoginService.save(thirdLogin);
HashMap<String, Object> retMap = new HashMap<String, Object>();
String appToken=JWTUtils.createAppToken(member.getId(),member.getPassword(),member.getDeviceId(),member.getAppLoginDate());
if(StringUtils.isNotEmpty(appToken) && appToken.startsWith("CharlesKeith ")){
RedisUtils.set(APP_TOKEN + member.getId(), appToken);
appToken=appToken.replaceFirst("CharlesKeith ","");
}
retMap.put("root", "login");
retMap.put(Constants.MOBILE, member.getMobile());
retMap.put(Constants.USERTYPE, member.getType().name());
retMap.put(Constants.TOKEN_PARAMETER_NAME, appToken);
retMap.put(Constants.USER_ID, member.getId());
retMap.put(Constants.USERNAME, member.getUsername());
retMap.put(Constants.REGISTER_LOCATION, member.getRegisterLocation());
retMap.put("valid",valid(member.getId()));
//添加購物車
addCart(request,member);
return BaseResponse.successResponse(retMap);
}catch (Exception e){
e.printStackTrace();
return BaseResponse.errorResponse();
}
}
當前端調(diào)用登錄返回map為"root":"bindOrRegister"信息時調(diào)用接口
首先校驗request信息不為空,從request中獲取到手機號和驗證碼,對驗證碼進行校驗。從request中獲取到pluginId,deviceId,openId,appType,unioId,根據(jù)appType判斷當不為微信小程序登錄時候?qū)luginId.openId校驗不能為空
根據(jù)手機號獲取到會員信息,會員信息不為空時進行登錄操作: 根據(jù)pluginId和會員Id查詢到該會員的第三方綁定信息,把第三方中的openId與request獲取的openId比較是否相同來判斷是否已經(jīng)綁定過該信息;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 對會員信息進行登錄校驗,異步調(diào)用活動發(fā)劵,小票同步信息, 根據(jù)會員信息和request的信息生成第三方綁定信息保存,生成token,返回"root":"login"和token信息和會員信息到Map中返回給前端跟新購物車信息
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 會員信息為空時進行注冊操作: ?根據(jù)request信息生成新會員信息,對會員信息進行注冊校驗保存會員信息,調(diào)用異步小票同步信息。根據(jù)會員信息和request的信息生成第三方綁定信息保存,生成token,返回"root":"login"和token信息和會員信息到Map中返回給前端更新購物車信息
以下邏輯是小程序授權(quán)登錄
一、獲取unionId
通過 wx.login 接口獲得臨時登錄憑證 code 后傳到開發(fā)者服務(wù)器調(diào)用此接口完成登錄流程
?
請求地址
?
GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
請求參數(shù)
屬性?? ?類型?? ?默認值?? ?必填?? ?說明
appid?? ?string?? ??? ?是?? ?小程序 appId
secret?? ?string?? ??? ?是?? ?小程序 appSecret
js_code?? ?string?? ??? ?是?? ?登錄時獲取的 code
grant_type?? ?string?? ??? ?是?? ?授權(quán)類型,此處只需填寫 authorization_code
返回值
Object
返回的 JSON 數(shù)據(jù)包
屬性?? ?類型?? ?說明
openid?? ?string?? ?用戶唯一標識
session_key?? ?string?? ?會話密鑰
unionid?? ?string?? ?用戶在開放平臺的唯一標識符,在滿足 UnionID 下發(fā)條件的情況下會返回,詳見 UnionID 機制說明。
errcode?? ?number?? ?錯誤碼
errmsg?? ?string?? ?錯誤信息
2.用戶未關(guān)注公眾號等相關(guān)主體賬號時,通過code獲取不到unionId時,則通過session_key、encryptedData、iv獲取unionId
encryptedData?? ?string?? ?包括敏感數(shù)據(jù)在內(nèi)的完整用戶信息的加密數(shù)據(jù)
iv?? ?string?? ?加密算法的初始向量
接口如果涉及敏感數(shù)據(jù)(如wx.getUserInfo當中的 openId 和 unionId),接口的明文內(nèi)容將不包含這些敏感數(shù)據(jù)。開發(fā)者如需要獲取敏感數(shù)據(jù),需要對接口返回的加密數(shù)據(jù)(encryptedData) 進行對稱解密。 解密算法如下:
對稱解密使用的算法為 AES-128-CBC,數(shù)據(jù)采用PKCS#7填充。
對稱解密的目標密文為 Base64_Decode(encryptedData)。
對稱解密秘鑰 aeskey = Base64_Decode(session_key), aeskey 是16字節(jié)。
對稱解密算法初始向量 為Base64_Decode(iv),其中iv由數(shù)據(jù)接口返回。
如接口 wx.getUserInfo 敏感數(shù)據(jù)當中的 watermark:
{
? ? "openId": "OPENID",
? ? "nickName": "NICKNAME",
? ? "gender": GENDER,
? ? "city": "CITY",
? ? "province": "PROVINCE",
? ? "country": "COUNTRY",
? ? "avatarUrl": "AVATARURL",
? ? "unionId": "UNIONID",
? ? "watermark":
? ? {
? ? ? ? "appid":"APPID",
? ? ? ? "timestamp":TIMESTAMP
? ? }
}
a.首先通過code獲取到session_key
b.通過session_key解密,從解密后的接送中提取unionId
二、獲取session_key
小程序點擊個人中心,首先會調(diào)thirdLogin/getSessionKey,后端通過傳遞的code獲取到session_key保存到redis,用于后續(xù)獲取手機號
session_key的過期時間通過小程序的checkSession檢查,不需要后端做任何操作,獲取到的session_key也不需要返還給小程序,只需自己保存即可
三、獲取手機號
文檔:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
小程序調(diào)thirdLogin/getPhoneNumber,傳給后端encryptedData,iv。后端通過之前redis保存的session_key解密數(shù)據(jù)
解密坑很多(前端必須將傳遞的數(shù)據(jù)通過encode處理,防止數(shù)據(jù)傳遞丟失,同時encryptedData,iv必須保證16個字節(jié)長度)
解密成功返給前端手機號,前端拿到手機號后通過手機號授權(quán)快速登錄
獲取得到的開放數(shù)據(jù)為以下 json 結(jié)構(gòu):文章來源:http://www.zghlxwxcb.cn/news/detail-841239.html
{
? ? "phoneNumber": "13580006666",
? ? "purePhoneNumber": "13580006666",
? ? "countryCode": "86",
? ? "watermark":
? ? {
? ? ? ? "appid":"APPID",
? ? ? ? "timestamp": TIMESTAMP
? ? }
}
參數(shù)?? ?類型?? ?說明
phoneNumber?? ?String?? ?用戶綁定的手機號(國外手機號會有區(qū)號)
purePhoneNumber?? ?String?? ?沒有區(qū)號的手機號
countryCode?? ?String?? ?區(qū)號
四、小程序通過手機號快速授權(quán)登錄
通過手機號查詢會員(手機號為之前解密傳給前端的手機號)
會員存在,直接將會員數(shù)據(jù)返回給小程序
會員不存在,直接注冊新會員,并將數(shù)據(jù)返回給小程序(小程序快捷登錄的不綁定第三方)
五、開發(fā)中的坑
前端傳過來的code,通過后臺請求微信接口的時候,code只能用一次,重復(fù)使用小程序報錯msg:code been used, hints
獲取手機號的時候,需要用到session_key,在剛開始通過code獲取到session_key的時候,可以放到redis里,等下次用的時候直接在redis拿,切忌不可將session_key返回前端然后在獲取手機號的時候通過前端傳遞
保存session_key的到redis時候,一定要記得redis的key要唯一,最開始我就是直接保存的,導(dǎo)致多個用戶同時登錄時候,只有一個能登錄成功,其實是因為他們共用了同一個session_key。一定要加以區(qū)分,可以把openId傳過來放入key中作為唯一約束
也許前端生成的加密數(shù)據(jù)和加密向量是正常的,直接復(fù)制給我們調(diào)微信接口可以獲取到數(shù)據(jù),但是在傳遞的時候可能會丟失字符,比如%等字符會解析成其他字符,可通過base64處理文章來源地址http://www.zghlxwxcb.cn/news/detail-841239.html
到了這里,關(guān)于app授權(quán)和小程序授權(quán)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!