Java实现微信小程序登录(服务端)

这篇具有很好参考价值的文章主要介绍了Java实现微信小程序登录(服务端)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        微信小程序的登录,这里只涉及到后端代码,所以默认你是申请了一个小程序并且有了appid和secret。不管怎么说,还是放上官方的文档链接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.htmlhttps://links.jianshu.com/go?to=https%3A%2F%2Fdevelopers.weixin.qq.com%2Fminiprogram%2Fdev%2Fframework%2Fopen-ability%2Flogin.html

Java实现微信小程序登录(服务端)

        上图要仔细的看一遍,这里简单的做一个说明,首先由小程序端调用wx.login()去获取code,然后,再通过wx.getUserInfo()去获取用户信息(这里请求login和getUserInfo是一起的,把这两次请求的数据合并发给服务端的login接口),通过请求,把:

1.code //临时登入凭证
// 如果不同意获取用户信息,则下面四个参数获取不到
2.rawData //用户非敏感信息,头像和昵称之类的
3.signature //签名
4.encryteDate //用户敏感信息,需要解密,(包含unionID)
5.iv //解密算法的向量

给到服务端,服务端根据 appid+secret+js_code+grant_type

去请求,获取到session_keyopenid(这里无法获取unionID),通过session_key,iv来解密encrypteDate获取用户敏感信息和unionID,把用户信息保存到数据库。然后,我们把sesssoin_key和openid保存下来,与token(自定义登入状态)来进行关联,最后把小程序需要的数据返回给小程序端,以后就通过token来维护用户登入状态。

用户表结构设计

CREATE TABLE `wechat_user` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `token` varchar(100) NOT NULL COMMENT 'token',
 `nickname` varchar(100) DEFAULT NULL COMMENT '用户昵称',
 `avatar_url` varchar(500) DEFAULT NULL COMMENT '用户头像',
 `gender` int(11) DEFAULT NULL COMMENT '性别  0-未知、1-男性、2-女性',
 `country` varchar(100) DEFAULT NULL COMMENT '所在国家',
 `province` varchar(100) DEFAULT NULL COMMENT '省份',
 `city` varchar(100) DEFAULT NULL COMMENT '城市',
 `mobile` varchar(100) DEFAULT NULL COMMENT '手机号码',
 `open_id` varchar(100) NOT NULL COMMENT '小程序openId',
 `union_id` varchar(100) DEFAULT '' COMMENT '小程序unionId',
 `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入时间',
 `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
 `deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
 PRIMARY KEY (`id`),
 KEY `idx_open_id` (`open_id`),
 KEY `idx_union_id` (`union_id`),
 KEY `idx_mobile` (`mobile`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='小程序用户表';

具体代码

说明,如果@Getter报错,那就删掉,自己加Getter,Setter,@Api开头的注解是swagger的注解,不需要的可以删掉
请求类

@ApiModel
@Getter
@Setter
public class WechatLoginRequest {
    @NotNull(message = "code不能为空")
    @ApiModelProperty(value = "微信code", required = true)
    private String code;
    @ApiModelProperty(value = "用户非敏感字段")
    private String rawData;
    @ApiModelProperty(value = "签名")
    private String signature;
    @ApiModelProperty(value = "用户敏感字段")
    private String encryptedData;
    @ApiModelProperty(value = "解密向量")
    private String iv;
}

非敏感信息DO

@Getter
@Setter
public class RawDataDO {
    private String nickName;
    private String avatarUrl;
    private Integer gender;
    private String city;
    private String country;
    private String province;
}

用户DO

@Getter
@Setter
public class WechatUserDO {
    private Integer id;
  
    private String token;
  
    private String nickname;

    private String avatarUrl;

    private Integer gender;

    private String country;

    private String province;

    private String city;

    private String mobile;

    private String openId;

    private String unionId;

    private String createdAt;

    private String updatedAt;
}

HttpClientUtils

public class HttpClientUtils {
  
    final static int TIMEOUT = 1000;

    final static int TIMEOUT_MSEC = 5 * 1000;   
  
    public static String doPost(String url, Map<String, String> paramMap) throws IOException {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";

        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);

            // 创建参数列表
            if (paramMap != null) {
                List<NameValuePair> paramList = new ArrayList<>();
                for (Entry<String, String> param : paramMap.entrySet()) {
                    paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
                }
                // 模拟表单
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
                httpPost.setEntity(entity);
            }

            httpPost.setConfig(builderRequestConfig());

            // 执行http请求
            response = httpClient.execute(httpPost);

            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                throw e;
            }
        }

        return resultString;
    }
  
    private static RequestConfig builderRequestConfig() {
        return RequestConfig.custom()
                .setConnectTimeout(TIMEOUT_MSEC)
                .setConnectionRequestTimeout(TIMEOUT_MSEC)
                .setSocketTimeout(TIMEOUT_MSEC).build();
    }
}

service

public interface WechatService {
    Map<String, Object> getUserInfoMap(WechatLoginRequest loginRequest) throws Exception;
}

Service impl

@Service
public class WechatServiceImpl implements WechatService {
    private static final String REQUEST_URL = "https://api.weixin.qq.com/sns/jscode2session";
    private static final String  = "authorization_code";
  
    @Override
    public Map<String, Object> getUserInfoMap(WechatLoginRequest loginRequest) throws Exception {
        Map<String, Object> userInfoMap = new HashMap<>();
        // logger报错的话,删掉就好,或者替换为自己的日志对象
        logger.info("Start get SessionKey,loginRequest的数据为:" + JSONObject.toJSONString(loginRequest));
        JSONObject sessionKeyOpenId = getSessionKeyOrOpenId(loginRequest.getCode());
        // 这里的ErrorCodeEnum是自定义错误字段,可以删除,用自己的方式处理
        Assert.isTrue(sessionKeyOpenId != null, ErrorCodeEnum.P01.getCode());

        // 获取openId && sessionKey
        String openId = sessionKeyOpenId.getString("openid");
        // 这里的ErrorCodeEnum是自定义错误字段,可以删除,用自己的方式处理
        Assert.isTrue(openId != null, ErrorCodeEnum.P01.getCode());
        String sessionKey = sessionKeyOpenId.getString("session_key");
        WechatUserDO insertOrUpdateDO = buildWechatUserDO(loginRequest, sessionKey, openId);

        // 根据code保存openId和sessionKey
        JSONObject sessionObj = new JSONObject();
        sessionObj.put("openId", openId);
        sessionObj.put("sessionKey", sessionKey);
        // 这里的set方法,自行导入自己项目的Redis,key自行替换,这里10表示10天
        stringJedisClientTem.set(WechatRedisPrefixConstant.USER_OPPEN_ID_AND_SESSION_KEY_PREFIX + loginRequest.getCode(),
                sessionObj.toJSONString(), 10, TimeUnit.DAYS);

        // 根据openid查询用户,这里的查询service自己写,就不贴出来了
        WechatUserDO user = wechatUserService.getByOpenId(openId);
        if (user == null) {
            // 用户不存在,insert用户,这里加了个分布式锁,防止insert重复用户,看自己的业务,决定要不要这段代码
            if (setLock(WechatRedisPrefixConstant.INSERT_USER_DISTRIBUTED_LOCK_PREFIX + openId, "1", 10)) {
              // 用户入库,service自己写
              insertOrUpdateDO.setToken(getToken())
              wechatUserService.save(insertOrUpdateDO);
              userInfoMap.put("token", insertOrUpdateDO.getToken())
            }
        } else {
            userInfoMap.put("token", wechatUser.getToken());
            // 已存在,做已存在的处理,如更新用户的头像,昵称等,根据openID更新,这里代码自己写
            wechatUserService.updateByOpenId(insertOrUpdateDO);
        }

        return userInfoMap;
    }
  
    // 这里的JSONObject是阿里的fastjson,自行maven导入
    private JSONObject getSessionKeyOrOpenId(String code) throws Exception {
        Map<String, String> requestUrlParam = new HashMap<>();
        // 小程序appId,自己补充
        requestUrlParam.put("appid", APPID);
        // 小程序secret,自己补充
        requestUrlParam.put("secret", SECRET);
        // 小程序端返回的code
        requestUrlParam.put("js_code", code);
        // 默认参数
        requestUrlParam.put("grant_type", GRANT_TYPE);

        // 发送post请求读取调用微信接口获取openid用户唯一标识
        String result = HttpClientUtils.doPost(REQUEST_URL, requestUrlParam);
        return JSON.parseObject(result);
    }
  
    private WechatUserDO buildWechatUserAuthInfoDO(WechatLoginRequest loginRequest, String sessionKey, String openId){
        WechatUserDO wechatUserDO = new WechatUserDO();
        wechatUserDO.setOpenId(openId);

        if (loginRequest.getRawData() != null) {
            RawDataDO rawDataDO = JSON.parseObject(loginRequest.getRawData(), RawDataDO.class);
            wechatUserDO.setNickname(rawDataDO.getNickName());
            wechatUserDO.setAvatarUrl(rawDataDO.getAvatarUrl());
            wechatUserDO.setGender(rawDataDO.getGender());
            wechatUserDO.setCity(rawDataDO.getCity());
            wechatUserDO.setCountry(rawDataDO.getCountry());
            wechatUserDO.setProvince(rawDataDO.getProvince());
        }

        // 解密加密信息,获取unionID
        if (loginRequest.getEncryptedData() != null){
            JSONObject encryptedData = getEncryptedData(loginRequest.getEncryptedData(), sessionKey, loginRequest.getIv());
            if (encryptedData != null){
                String unionId = encryptedData.getString("unionId");
                wechatUserDO.setUnionId(unionId);
            }
        }

        return wechatUserDO;
    }
  
    private JSONObject getEncryptedData(String encryptedData, String sessionkey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionkey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位,那么就补足.这个if中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + 1;
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JSONObject.parseObject(result);
            }
        } catch (Exception e) {
            logger.error("解密加密信息报错", e.getMessage());
        }
        return null;
    }
  
    private boolean setLock(String key, String value, long expire) throws Exception {
        boolean result = stringJedisClientTem.setNx(key, value, expire, TimeUnit.SECONDS);
        return result;
    }
  
    private String getToken() throws Exception {
        // 这里自定义token生成策略,可以用UUID+sale进行MD5
        return "";
    }
}

Controller

@RestController("LoginController")
@RequestMapping(value = "/wechat/login")
public class LoginController {
    @Resource
    WechatService wechatService;
    
    @ApiOperation(value = "1.登入接口", httpMethod = "POST")
    @PostMapping("/save")
    public Map<String, Object> login(
            @Validated @RequestBody WechatLoginRequest loginRequest) throws Exception {

        Map<String, Object> userInfoMap = wechatService.getUserInfoMap(loginRequest);
        return userInfoMap;
    }
}

写在最后

一些注意事项:

  • code是有时效行的,5分钟内有效,并且只能使用一次
  • token的实现,以及token过期时间,token放在数据库中还是缓存中,token是否每次登入都需要刷新?这么些个问题,自己结合业务需求来做判断,我这里为了简单起见,直接放数据库里了

#@lehao#

帮助到您请点赞关注收藏谢谢!!


 文章来源地址https://www.toymoban.com/news/detail-429169.html

到了这里,关于Java实现微信小程序登录(服务端)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【微信小程序】实现微信小程序登录(附源码)

    通过 点击登录按钮 , 调用微信接口 wx.getUserProfile拿到微信的个人信息,先 检查是否之前已经登录 ,若没登录则将拿到的个人信息调用后台的接口,把个人信息传给后台, 登录成功之后 把相关信息存储在app.globalData中共享给全局使用 (这里使用微信云开发作为后台,提前建

    2024年02月11日
    浏览(49)
  • node微信小程序登录实现登录

    node微信小程序登录实现登录 好久不见兄弟们这段时间比较忙 需要使用到的模块 废话少说直接上可cv(bai piao)代码 下面展示我的完整代码

    2024年02月09日
    浏览(46)
  • 微信小程序,如何实现登录

    业务流程: 1:首先需要一个按钮触发事件 2:调用微信小程序的登录接口wx.login,拿到code 3:调用微信小程序的获取用户信息的接口wx.getUserProfile,拿到用户的个人信息(包括用的昵称以及头像) 4:拿到的个人信息调用后台的接口,八个人信息传给后台,登陆成功并把相关喜喜

    2024年02月03日
    浏览(37)
  • 微信小程序 如何实现登录

    业务流程: 1:首先需要一个按钮触发事件 2:调用微信小程序的登录接口 wx.login ,拿到code 3:调用微信小程序的获取用户信息的接口 wx.getUserProfile ,拿到用户的个人信息 4: 拿到的个人信息调用后台的接口,把个人信息传给后台,登录成功并把相关信息存储在本地的缓存中

    2024年02月09日
    浏览(78)
  • Java - 微信小程序授权手机号登录

            最近做了一个关于商城的项目,B端选用若依的开源框架,C端还是vue前后端分离。其中C端主要是小程序的形式,所以想着来总结一下对接微信小程序登录中Java部分遇到的坑即代码分享! 废话不多说,直接上代码! 1、controller 层代码          入参我这边是直接使用

    2024年02月04日
    浏览(50)
  • 微信小程序 + Java + Mysql(登录 Demo)

    目录 一、开发前准备 二、设计登录页面(小程序) 三、创建 Maven 项目 四、创建数据库与数据表 五、后台代码(Java Servlet) 六、运行 七、总结 建议  1、注册一个微信开发者账号:https://open.weixin.qq.com (略)  2、开发工具:idea、Mysql、微信开发者工具  3、应用技术:Ja

    2024年02月03日
    浏览(35)
  • 基于Java+Vue+uniapp微信小程序智能社区服务平台设计和实现

    博主介绍 : ✌ 全网粉丝30W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精彩专栏 推荐订阅 👇🏻 不然下次找不到哟 2022-2024年

    2024年02月05日
    浏览(42)
  • 基于Java+Vue+uniapp微信小程序医院综合服务平台设计和实现

    博主介绍 : ✌ 全网粉丝30W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行交流合作 ✌ 主要内容: SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、P

    2024年02月04日
    浏览(104)
  • 微信小程序实现简单登录界面和登录功能

    客户端开发和学习过程中,登录功能是一个很常见的场景。本文将介绍,微信小程序开发过程中是如何实现登录界面和登录功能的。 话不多说,直接上代码。 (1)index.js文件,代码如下: (2)index.wxml文件,代码如下: (3)index.wxss文件,代码如下: 运行结果如下: 本文介绍了微信

    2024年02月12日
    浏览(83)
  • 【微信小程序】实现微信小程序登录(附源码)后端,微信小程序获取手机号

    登录简介 第一步:获取token 第二步:通过token拿用户信息 第三步:调用接口获取手机号 HttpClientUtil: WeChatUtil: controller层: service层: serviceImpl层: 登录简介        新版本微信小程序登录 是前端获取用户信息,不再是后端获取信息进行保存。所以后端要做的主要流程就是

    2024年04月23日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包