前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java开发微信小程序登录接口

Java开发微信小程序登录接口

作者头像
sunonzj
发布2022-06-21 14:41:20
2.7K0
发布2022-06-21 14:41:20
举报
文章被收录于专栏:zjblogzjblog

先说一下需求吧,小程序微信登录,用户授权获取个人信息。然后保存用户基本信息到系统用户表,同时新增用户账户信息,上传用户头像。 emmm..之所以想写下来是因为自己踩过的坑啊。。就不细说了。 链接: 小程序微信登录官方文档

登录:

  1. 小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
  2. 开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。

之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

用户授权获取个人信息:

wx.getUserInfo(OBJECT)

注意:此接口有调整,使用该接口将不再出现授权弹窗,请使用 <button open-type="getUserInfo"></button> 引导用户主动进行授权操作

  1. 当用户未授权过,调用该接口将直接进入fail回调
  2. 当用户授权过,可以使用该接口获取用户信息

注:当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。

这是官方给的文档写的。就是前端调用微信接口得到一个code和useInfo,把这两个给后台,后台首先通过code走微信提供的链接获取到用户唯一标识openid 和 会话密钥session_key。

代码语言:javascript
复制
String token = HttpUtil.doPost("n 
?appid=" + prop.get(APP_ID)+ "&secret=" + prop.get(API_KEY) +
 "&js_code=" + code + "&grant_type=authorization_code", null, "UTF-8");

效验成功返回一个

代码语言:javascript
复制
//正常返回的JSON数据包{  	"openid": "OPENID",  	"session_key": "SESSIONKEY"}

效验失败返回

代码语言:javascript
复制
//错误时返回JSON数据包(示例为Code无效){	"errcode": 40029,	"errmsg": "invalid
 code"}

效验成功后,然后是根据参数useInfo获取用户的信息。利用上面得到的openId和sessionkey

小程序可以通过各种前端接口获取微信提供的开放数据。 考虑到开发者服务器也需要获取这些开放数据,微信会对这些数据做签名和加密处理。 开发者后台拿到开放数据后可以对数据进行校验签名和解密,来保证数据不被篡改。

为了确保 开放接口 返回用户数据的安全性,微信会对明文数据进行签名。开发者可以根据业务需要对数据包进行签名校验,确保数据的完整性。

  1. 通过调用接口(如 wx.getUserInfo)获取数据时,接口会同时返回 rawData、signature,其中 signature = sha1( rawData + session_key )
  2. 开发者将 signature、rawData 发送到开发者服务器进行校验。服务器利用用户对应的 session_key 使用相同的算法计算出签名 signature2 ,比对 signature 与 signature2 即可校验数据的完整性。
代码语言:javascript
复制
JSONObject userInfo = JSONObject.parseObject(userInfo);
        // 用户信息
        // 签名校验
        if (!new String(SecureUtil.sha1X16(userInfo.get(WX_RAWDATA) 
        + sessionKey, ENCODING_FORMAT), ENCODING_FORMAT)
            .equals(userInfo.getString(WX_SIGNATURE))) {
            renderJson(Result.fail("签名校验失败").build());
            return;
        }

签名效验成功后,解密加密的数据

代码语言:javascript
复制
// 加密数据解密
        JSONObject userInfoEncrypted =
            desEncrypt(userInfo.getString(WX_ENCRYPTEDDATA), sessionKey,
             userInfo.getString(WX_IV));
        if (null == userInfoEncrypted) {
            renderJson(Result.fail("数字水印校验失败").build());
            return;
        }

解密完后就是根据自己的需求开发了。

先判断第三方的openid在数据库中是否存在,如果存在则说明用户已经登录过小程序,如果数据库中不存在则用户是首次登录小程序需要将用户信息保存到数据库

代码语言:javascript
复制
 // 判断是否已注册
        String openId = userInfoEncrypted.getString(WX_USERINFO_OPEN_ID);
        MemberUser user = memberUserService.find(new MemberUser().setOpenId(openId));
        //已注册
        if (Tools.isNotEmpty(user)) {
            MemberInfo info = memberInfoService.find(new MemberInfo()
            .setUserId(user.getUserId()));
            result = assembleResult(user, info, userInfoEncrypted
            .getString(WX_USERINFO_NICKNAME), sessionKey);
            renderJson(Result.ok().put(result).build());
            return;
        }

如果未注册过,就进行注册绑定,把openId保存到第三方绑定表,然后主键为用户表的外键。同时新增用户账户数据。把头像上传到自己服务器。

设置微信手机号绑定一样的。

注意:

1、加密解密需要注意,userinfo和绑定手机号需要的phoneinfo都需要效验签名解密获得。效验解密方法官方文档也给出了例子。

2、返回给前端的openId需要加密处理,防止被篡改。

3、注意微信昵称的

完整代码贴到下面,前端代码我就不说了,登录接口需要前端给两个参数:

1、code (登录凭证)

code

String

用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 api,使用 code 换取 openid 和 session_key 等信息

2、 userInfo(用户信息)

rawData

String

不包括敏感信息的原始数据字符串,用于计算签名。

signature

String

使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,参考文档 signature。

encryptedData

String

包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法

iv

String

加密算法的初始向量,详细见加密数据解密算法

UserInfo参数:

nickName

String

用户昵称

avatarUrl

String

用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表132*132正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。

gender

String

用户的性别,值为1时是男性,值为2时是女性,值为0时是未知

city

String

用户所在城市

province

String

用户所在省份

country

String

用户所在国家

language

String

用户的语言,简体中文为zh_CN

完整代码:

代码语言:javascript
复制
//参数:
 @Params(value = {@Param(name = "code", dataType = "string", 
 in = "formData", description = "code"),
        @Param(name = "userInfo", dataType = "string",
         in = "formData", description = "微信获取用户信息的数据", 
         required = true)})
       // 登录:
        public void login() throws Exception {
        Map<String, Object> result = new HashMap<String, Object>();
        // 获取session_key
        Result sessionKeyMap = code2sessionKey(getPara("code"));
        String sessionKey = "";
        if ((boolean)sessionKeyMap.get(Const.RESULT)) {
            sessionKey = String.valueOf(sessionKeyMap.
            get(Const.PROMPT_MSG));
        } else {
            renderJson(sessionKeyMap);
            return;
        }
        JSONObject userInfo = JSONObject.parseObject(getPara("userInfo"));
        // 用户信息
        // 签名校验
        if (!new String(SecureUtil.sha1X16(userInfo.get(WX_RAWDATA)
         + sessionKey, ENCODING_FORMAT), ENCODING_FORMAT)
            .equals(userInfo.getString(WX_SIGNATURE))) {
            renderJson(Result.fail("签名校验失败").build());
            return;
        }

        // 加密数据解密
        JSONObject userInfoEncrypted =
            desEncrypt(userInfo.getString(WX_ENCRYPTEDDATA),
             sessionKey, userInfo.getString(WX_IV));
        if (null == userInfoEncrypted) {
            renderJson(Result.fail("数字水印校验失败").build());
            return;
        }

        // 判断是否已注册
        String openId = userInfoEncrypted.getString(WX_USERINFO_OPEN_ID);
        MerchantThird user = merchantThirdService.
        find(new MerchantThird().setOpenId(openId));
        if (Tools.isNotEmpty(user)) {
            MerchantInfo info = merchantInfoService
            .findById(user.getMerchantId());
            if (info == null) {
                return;
            }
            SysUser sysUser = sysUserService
            .findById(info.getUserId());

            result = assembleResult(user, info, sysUser, 
            userInfoEncrypted.getString(WX_USERINFO_NICKNAME),
             sessionKey);
            renderJson(Result.ok().put(result).build());
            return;
        }
        result.put("msg", "该用户没有注册为医生");
        result.put("token", JWTUtil.createToken(openId, sessionKey));
        renderJson(Result.ok().put(result).build());
    }

        /**
     * 微信code换取sessionKey等加密信息
     * 
     * @param code
     * @return
     */
    private Result code2sessionKey(String code) {

        String token = HttpUtil.doPost("https://api.weixin.qq.com/sn
        s/jscode2session?appid=" + prop.get(APP_ID)
            + "&secret=" + prop.get(API_KEY) + "&js_code="
             + code + "&grant_type=authorization_code", null,
              "UTF-8");
        JSONObject jsonObject = JSONObject.parseObject(token);
        if (null != jsonObject.get(WX_ERROR_CODE)) {
            return Result.fail(jsonObject.getString(WX_ERROR_MSG) 
            + "(" + jsonObject.getString(WX_ERROR_CODE) + ")")
                .build();
        } else {
            return Result.ok(jsonObject.getString(WX_SESSION_KEY))
            .build();
        }
    }
  /**
     * 聚合用户账号信息
     */
    private MemberUser assembleMemberUser(JSONObject userInfoEncrypted, Date date) {
        return new MemberUser().setUserId(Tools.getUniqueId())
        .setCreationTime(date).setUpdateTime(date)
            .setStatus(Const.DEFAULT_STATUS)
            .setOpenId(userInfoEncrypted.getString(WX_USERINFO_OPEN_ID));
    }

    /**
     * 聚合用户信息
     * 
     * @param userInfoEncrypted 参数集
     * @param date 日期
     * @param userId
     * @return
     */
    private MemberInfo assembleMemberInfo(JSONObject userInfoEncrypted,
     Date date, Long userId) {
        return new MemberInfo().setMemberId(Tools.getUniqueId())
            .setName(filterEmoji(userInfoEncrypted.getString(WX_USERINFO_NICKNAME)
            ,"*")).setType(Const.DEFAULT_STATUS)
            .setAvatar(Tools.getUniqueId()).setSex(userInfoEncrypted.getString
            ("gender").equals("2") ? 1 : 0)
            .setCreationTime(date).setUpdateTime(date).setUserId(userId)
            .setInvitationCode(String.valueOf(Tools.getRandomNum(10000000,
             99999999)));
    }
  /**
     * 微信水印解密校验
     * 
     * @param data 加密字符串
     * @param key 密钥
     * @param iv 加密偏移量
     * @return
     * @throws UnsupportedEncodingException
     */
    private JSONObject desEncrypt(String data, String key, String iv) throws
     UnsupportedEncodingException {
        byte[] dataByte =
            AesEncryptUtil.decryptOfDiyIV(Base64.decodeBase64(data),
             Base64.decodeBase64(key), Base64.decodeBase64(iv));
        JSONObject result = JSONObject.parseObject(new String(dataByte, "UTF-8"));
        // 水印校验
        JSONObject watermark = JSONObject.parseObject(String.valueOf(result.
        get(WX_WATERMARK)));
        if (!watermark.getString(WX_WATERMARK_APP_ID).equals(prop.get(APP_ID))) {
            return null;
        }
        return result;
    }
    /**
     * 
     * @param user 返回的用户账户信息
     * @param info 返回的token内的信息(仅含memberId和openId)
     * @param nickName 返回的昵称
     * @param result 返回的额外内容
     * @return
     */
    private Map<String, Object> assembleResult(MemberUser user, MemberInfo info,
     String nickName, String sessionKey) {
        Map<String, Object> result = new HashMap<String, Object>(6);
        if (Tools.isEmpty(user) || Tools.isEmpty(info)) {
            result.put(Const.RESULT, false);
            result.put(Const.PROMPT_MSG, "获取参数异常");
        }
        info.put(Const.OPEN_ID, user.getOpenId());
        info.put(Const.SESSION_KEY, sessionKey);
        user.remove("openId");
        user.setName(nickName);
        setAvatarMemberUser(user, info);
        result.put("user", user);
        result.put("accountInfo", memberAccountService.find(new MemberAccount()
        .setMemberId(info.getMemberId())));
        result.put("integralInfo", memberIntegralService.find(new MemberIntegral()
        .setMemberId(info.getMemberId())));
        result.put("token", JWTUtil.createToken(info));
        return result;
    }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-09-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • wx.getUserInfo(OBJECT)
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档