uniapp 获取openid 实现登录、uniapp获取微信小程序手机号、javax.crypto.BadPaddingException: pad block corrupted

获取openid流程

小程序实现登录的流程,是先通过wx.login获取code,然后再用code请求自己服务器,自己服务器拿着code去微信服务器获取openid,然后业务自定义实现登录,微信小程序登录流程图
在这里插入图片描述

uniapp实现登录与上述流程是一样的,只是对登录实现了封装,用uni.login替代了wx.login

uni.login({
  provider: 'weixin',
  success: function (loginRes) {
   //loginRes中有code,拿着code再请求自己服务器以获取openid
    console.log(loginRes.authResult);
  }
});

开发者服务器拿着code,请求微信服务器auth.code2Session接口

GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

js_code=JSCODE就是上文说的code,
这个接口就会返回openid、session_key。

  • openid
    • 用户的身份标识,每个用户在每个公众号或者小程序中都有一个唯一openid,跨公众号/小程序时openid不相同
  • session_key

获取openid样例代码

基本流程是,小程序/公众号端使用uni.login获取到code,传到后台,后台使用appid、appsecret、code获取到openid、unionid、session_key

小程序端

uni.login可以静默执行,也就是用户无感的情况下执行。

uni.login({ //微信授权
	service: 'oauth',
	success: function(res) {
		//登录的code
		console.log(res.code)
	},
	fail(err) {
		uni.showToast({
			title: '获取失败',
			duration: 3000,
			icon: 'none'
		})
	}
});

后端

class Controller{

    @Value("${mp.appid}")
    private String appid;
    @Value("${mp.appsecret}")
    private String appsecret;
    @Autowired
    private RestTemplate restTemplate;

	@PostMapping("getOpenid")
	public void getOpenid(String loginCode){
		String code2SessionRespStr = restTemplate.getForObject("https://api.weixin.qq.com/sns/jscode2session?appid={APPID}&secret={SECRET}&js_code={JSCODE}&grant_type=authorization_code",
		                String.class,
		                appid,
		                appsecret,
		                loginCode);
	}
 WxCode2SessionResp code2SessionResp = JSONObject.parseObject(code2SessionRespStr,WxCode2SessionResp.class);
}
public class WxCode2SessionResp {
    private String openid;//	string	用户唯一标识
    private String session_key;//	string	会话密钥
    private String unionid;//		string	用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。
    private String errcode;//		number	错误码
    private String errmsg;//		string	错误信息
}

注意,如果是双卡手机,两个手机号的openid是相同的

小程序获取手机号

微信公众号没有获取手机号的api,似乎可以通过卡券之类的途径间接得到手机号,但总归很麻烦;小程序提供了获取手机号api,流程是前端通过用户授权获取到code,后台通过先获取access_token,再根据access_tokencode获取手机号;样例代码

小程序端代码

获取手机号不能静默执行,必须由用户授权,所以有个按钮,由用户点击触发,因为api升级原因,存在低版本不支持情况,所以增加了提示。

<button type="default"  open-type="getPhoneNumber"
				@getphonenumber="onGetphone">微信授权登录</button>

//获取用户手机号码
onGetphone(e) {
	console.log("获取手机号回调参数",e)
	if (e.detail.errMsg == 'getPhoneNumber:ok') {
		console.log('用户点击了接受');
		//获取手机号的code
		const {detail:{code:getPhoneCode}} = e
		if(!getPhoneCode){
			uni.showToast({
			  title:"微信版本过低,请升级",
			  duration: 3000,
			  icon: 'none'
			})
			return 
		}
		//发送ajax请求,将code提交到后台
	} 
},

后端代码

class Controller{

    @Value("${mp.appid}")
    private String appid;
    @Value("${mp.appsecret}")
    private String appsecret;
    @Autowired
    private RestTemplate restTemplate;
    
@PostMapping("getPhone")
	public void getPhone(String getPhoneCode){
		String wxAccessTokenRespStr = restTemplate
		                .getForObject("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={appsecret}",
		                        String.class,
		                        appid,
		                        appsecret);
        log.info("获取小程序accessToken返回值是:{}", wxAccessTokenRespStr);
        GetWxAccessTokenResp wxAccessTokenResp = JSONObject.parseObject(wxAccessTokenRespStr,GetWxAccessTokenResp.class);
        Assert.hasText(wxAccessTokenResp.getAccess_token(), wxAccessTokenResp.getErrmsg());
        String access_token = wxAccessTokenResp.getAccess_token();
        GetWxPhoneCommand getWxPhoneCommand = new GetWxPhoneCommand();
        getWxPhoneCommand.setCode(getPhoneCode);
        String getWxPhoneRespStr = restTemplate
                .postForObject("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={ACCESS_TOKEN}",
                        getWxPhoneCommand, String.class, access_token);
        log.info("获取小程序手机号返回值是:{}", getWxPhoneRespStr);
        GetWxPhoneResp getWxPhoneResp = JSONObject.parseObject(getWxPhoneRespStr,GetWxPhoneResp.class);
public class GetWxAccessTokenResp {
    private String access_token;//	string	获取到的凭证
    private String expires_in;//	number	凭证有效时间,单位:秒。目前是7200秒之内的值。
    private String errCode;//	number	错误码
    private String errmsg;//	string	错误信息
}
public class GetWxPhoneCommand {
    private String code;
}
public class GetWxPhoneResp {

    private String errcode;//	number	错误码
    private String errmsg;//		string	错误提示信息
    private PhoneInfo phoneInfo;//		Object	用户手机号信息

    
    public static class PhoneInfo{
        private String phoneNumber;//	string	用户绑定的手机号(国外手机号会有区号)
        private String purePhoneNumber;//	string	没有区号的手机号
        private String countryCode;//	string	区号
        private Watermark watermark;//	Object	数据水印
    }
    
    public static class Watermark{
        private String appid;//		string	小程序appid
        private String timestamp;//		number	用户获取手机号操作的时间戳

    }
}

问题

在实践中,会遇到javax.crypto.BadPaddingException: pad block corrupted ,这是因为在获取手机号的回调中,调用uni.login,uni.login会冲掉解密手机号用到的session_key,可参考会话密钥 session_key 有效性,这个问题有两个解决办法

  • uni.login先于获取手机号执行,比如在onShow()、created()中执行,仍然用login的code作为引子来解密手机号,缺点就是混在一起,耦合了;代码不再举例;
  • 手机号code和login的code拆开,各行其是,也就是说不用uni.login的code来解密手机号回调中的encryptedData,login的code只用来获取openid;手机号的code用来获取手机号;此种方式时,可以在获取手机号回调中执行uni.login。代码就是上边的那些稍微整合一些就可以。

总结

获取login的code和获取手机号的code两回事,不要弄混。

  • login的code用来获取openid,uni.login可以静默执行,用户无感知
  • 手机号的code用来获取手机号,必须由用户授权

参考

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值