- 【微信开发】SpringBoot 集成微信小程序授权登录
- 1、SprinBoot 后端
- (1)准备工作
- (2)相关配置类
- (3)相关实体类
- (4)处理后端逻辑
- 2、Uniapp 前端
- (1)授权登录
- (2)效果样式
- 微信公众号
我这里采用了第三方的依赖,目前是最火的微信开发工具吧,WxJava
1、SprinBoot 后端 (1)准备工作引入相关依赖
com.github.binarywang weixin-java-pay4.1.0
配置application.yml
# ----------------------系统配置 # 业务配置 pay-platform: # 微信 wx: pay: appId: secret: mchId: mchKey: keyPath: notifyUrl:(2)相关配置类
属性类
import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; @Data @ConfigurationProperties(prefix = "pay-platform.wx.pay") public class WxPayProperties { private String appId; private String secret; private String mchId; private String mchKey; private String subAppId; private String subMchId; private String keyPath; private String notifyUrl; }
属性配置类
import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; import lombok.AllArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnClass(WxPayService.class) @EnableConfigurationProperties(WxPayProperties.class) @AllArgsConstructor public class WxPayConfiguration { private WxPayProperties properties; @Bean @ConditionalOnMissingBean public WxPayService wxService() { WxPayConfig payConfig = new WxPayConfig(); payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId())); payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId())); payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey())); payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId())); payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId())); payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath())); // 可以指定是否使用沙箱环境 payConfig.setUseSandboxEnv(false); WxPayService wxPayService = new WxPayServiceImpl(); wxPayService.setConfig(payConfig); return wxPayService; } }(3)相关实体类
相关实体类,都可以使用json的map格式来处理,我这里是个人习惯
import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true) public class AccessToken { private String accessToken; private Integer expiresIn; private Integer errCode; private String errMsg; }
import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true) public class Code2Session { private String openId; private String sessionKey; private String unionId; private Integer errCode; private String errMsg; }
import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true) public class WeiXinLogin { private String code; private String encryptedData; private String iv; private String nickName; private String avatarUrl; private Integer gender; }
lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true) public class WeiXinToken { public static String token; }(4)处理后端逻辑
控制层
package com.ruoyi.business.appuser.controller; import cn.hutool.core.lang.Validator; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.business.appuser.service.WeiXinService; import com.ruoyi.business.appuser.vo.wx.Code2Session; import com.ruoyi.business.appuser.vo.wx.OrderInfo; import com.ruoyi.business.appuser.vo.wx.WeiXinLogin; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.framework.web.service.SysLoginService; import com.ruoyi.framework.web.service.SysPermissionService; import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.service.ISysUserService; import com.zhhy.tool.utils.IntegerUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.base64; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.spec.AlgorithmParameterSpec; import java.util.List; import java.util.Set; @Slf4j @Api(value = "微信API", tags = {"微信API"}) @RestController @RequestMapping("/au/weiXin") public class AuWeiXinController { @Autowired private ISysUserService userService; @Autowired private SysLoginService loginService; @Autowired private WeiXinService weiXinService; @Autowired private SysPermissionService permissionService; @Autowired private TokenService tokenService; @ApiOperation("微信用户登录") @PostMapping("login") public AjaxResult login(@RequestBody WeiXinLogin dto) { Code2Session code2Session = weiXinService.code2Session(dto.getCode()); if (StringUtils.isNotEmpty(code2Session.getOpenId())) { // 解析电话号码 String phoneNumber; byte[] byEncrypdata = base64.decodebase64(dto.getEncryptedData()); byte[] byIvdata = base64.decodebase64(dto.getIv()); byte[] bySessionkey = base64.decodebase64(code2Session.getSessionKey()); AlgorithmParameterSpec ivSpec = new IvParameterSpec(byIvdata); try { SecretKeySpec keySpec = new SecretKeySpec(bySessionkey, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); String phoneResult = new String(cipher.doFinal(byEncrypdata), StandardCharsets.UTF_8); JSONObject phoneObject = JSONObject.parseObject(phoneResult); phoneNumber = phoneObject.getString("phoneNumber"); } catch (Exception e) { e.printStackTrace(); return AjaxResult.error("手机号码解密失败"); } // 根据openId查询是否存在这个用户 Listlist = userService.list(new LambdaQueryWrapper ().eq(SysUser::getOpenId, code2Session.getOpenId()) .or().eq(SysUser::getUserName, phoneNumber).or().eq(SysUser::getPhonenumber, phoneNumber)); AjaxResult ajax = AjaxResult.success(); if (CollectionUtils.isEmpty(list)) { // 添加新用户 String defaultPassword = "111111"; SysUser user = new SysUser() .setOpenId(code2Session.getOpenId()) .setUserName(phoneNumber) .setNickName(dto.getNickName()) .setDeptId(0L) .setPassword(defaultPassword) .setPhonenumber(phoneNumber) .setAvatar(dto.getAvatarUrl()); if (IntegerUtils.eq(dto.getGender(), 0)) { user.setSex("2"); } else if (IntegerUtils.eq(dto.getGender(), 1)) { user.setSex("0"); } else if (IntegerUtils.eq(dto.getGender(), 2)) { user.setSex("1"); } if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName()))) { return AjaxResult.error("手机号已被注册"); } else if (Validator.isNotEmpty(user.getPhonenumber()) && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { return AjaxResult.error("手机号已被使用"); } user.setCreateBy(SecurityUtils.getUsername()); user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); // 默认给角色用户 user.setRoleIds(new Long[]{1L}); userService.insertUser(user); String token = loginService.login(user.getUserName(), defaultPassword); ajax.put(Constants.TOKEN, token); return ajax; } else if (list.size() == 1) { // 更新用户信息:这里查询出的一个信息,可能是openId、userName、phonenumber三个字段其中某个查出来的 SysUser sysUser = list.get(0); sysUser.setNickName(dto.getNickName()); sysUser.setAvatar(dto.getAvatarUrl()); if (IntegerUtils.eq(dto.getGender(), 0)) { sysUser.setSex("2"); } else if (IntegerUtils.eq(dto.getGender(), 1)) { sysUser.setSex("0"); } else if (IntegerUtils.eq(dto.getGender(), 2)) { sysUser.setSex("1"); } if (StringUtils.isEmpty(sysUser.getOpenId())) { sysUser.setOpenId(code2Session.getOpenId()); } userService.updateById(sysUser); SysUser user = userService.selectUserByUserName(sysUser.getUserName()); LoginUser loginUser = new LoginUser(user, permissionService.getMenuPermission(user)); String token = tokenService.createToken(loginUser); ajax.put(Constants.TOKEN, token); return ajax; } else { return AjaxResult.error("用户信息异常,存在多个openId或电话号码"); } } else { return AjaxResult.error(code2Session.getErrMsg()); } } @ApiOperation("获取用户信息") @GetMapping("getInfo") public AjaxResult getInfo() { LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); SysUser user = loginUser.getUser(); AjaxResult ajax = AjaxResult.success(); Set permission = permissionService.getRolePermission(user); ajax.put("user", user); ajax.put("roles", permission); ajax.put("permissions", permissionService.getMenuPermission(user)); return ajax; } }
业务处理层
import com.ruoyi.business.appuser.vo.wx.AccessToken; import com.ruoyi.business.appuser.vo.wx.Code2Session; import com.ruoyi.business.appuser.vo.wx.OrderInfo; import com.ruoyi.common.core.domain.AjaxResult; public interface WeiXinService { Code2Session code2Session(String code); AccessToken getAccessToken(); }
import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.request.baseWxPayRequest; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.bean.result.baseWxPayResult; import com.github.binarywang.wxpay.constant.WxPayConstants; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; import com.ruoyi.business.appuser.config.WxPayProperties; import com.ruoyi.business.appuser.service.WeiXinService; import com.ruoyi.business.appuser.vo.wx.AccessToken; import com.ruoyi.business.appuser.vo.wx.Code2Session; import com.ruoyi.business.appuser.vo.wx.OrderInfo; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.utils.ServletUtils; import com.zhhy.tool.utils.IntegerUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID; @Slf4j @Service @AllArgsConstructor public class WeiXinServiceImpl implements WeiXinService { private WxPayService wxPayService; private WxPayProperties wxPayProperties; @Override public Code2Session code2Session(String code) { String url = "https://api.weixin.qq.com/sns/jscode2session?" + "appid=" + wxPayProperties.getAppId() + "&secret=" + wxPayProperties.getSecret() + "&js_code=" + code + "&grant_type=authorization_code"; String result = HttpUtil.get(url); JSONObject jsonObject = JSONObject.parseObject(result); Code2Session code2Session = new Code2Session().setOpenId(jsonObject.getString("openid")) .setSessionKey(jsonObject.getString("session_key")) .setUnionId(jsonObject.getString("unionid")) .setErrCode(jsonObject.getInteger("errcode")) .setErrMsg(jsonObject.getString("errmsg")); if (StringUtils.isEmpty(code2Session.getOpenId())) { code2Session.setErrMsg("OpenId为空"); } else if (IntegerUtils.eq(code2Session.getErrCode(), -1)) { code2Session.setErrMsg("系统繁忙,此时请开发者稍候再试"); } else if (IntegerUtils.eq(code2Session.getErrCode(), 40029)) { code2Session.setErrMsg("code 无效"); } else if (IntegerUtils.eq(code2Session.getErrCode(), 45011)) { code2Session.setErrMsg("频率限制,每个用户每分钟100次"); } else { code2Session.setErrMsg("其他错误"); } return code2Session; } @Override public AccessToken getAccessToken() { String url = "https://api.weixin.qq.com/cgi-bin/token?" + "grant_type=client_credential" + "&appid=" + wxPayProperties.getAppId() + "&secret=" + wxPayProperties.getSecret(); String result = HttpUtil.get(url); JSONObject jsonObject = JSONObject.parseObject(result); AccessToken accessToken = new AccessToken().setAccessToken(jsonObject.getString("access_token")) .setExpiresIn(jsonObject.getInteger("expires_in")) .setErrCode(jsonObject.getInteger("errcode")) .setErrMsg(jsonObject.getString("errmsg")); if (StringUtils.isEmpty(accessToken.getAccessToken())) { } else if (IntegerUtils.eq(accessToken.getErrCode(), -1)) { accessToken.setErrMsg("系统繁忙,此时请开发者稍候再试"); } else if (IntegerUtils.eq(accessToken.getErrCode(), 40001)) { accessToken.setErrMsg("AppSecret 错误或者 AppSecret 不属于这个小程序,请开发者确认 AppSecret 的正确性"); } else if (IntegerUtils.eq(accessToken.getErrCode(), 40002)) { accessToken.setErrMsg("请确保 grant_type 字段值为 client_credential"); } else if (IntegerUtils.eq(accessToken.getErrCode(), 40013)) { accessToken.setErrMsg("不合法的 AppID,请开发者检查 AppID 的正确性,避免异常字符,注意大小写"); } else { accessToken.setErrMsg("其他错误"); } return accessToken; } }2、Uniapp 前端 (1)授权登录
一个页面直接搞定,授权登录,换取token,通过token查询用户信息。请求使用的是uview的官网工具类
(2)效果样式 微信公众号申请获取以下权限 获得你的公开信息(昵称,头像、地区等) 获得你微信绑定的手机号
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)