对接微信小程序授权登录
文章目录
- 一. 微信小程序授权登录
- 引入依赖
- 修改配置文件
- 配置类Properties
- 配置类Config
- Controller控制器代码
- Service接口层代码
- ServiceImpl实现层代码
- 统一响应类 R
- 具体授权登录代码
一. 微信小程序授权登录
引入依赖
<!--微信小程序-->
<dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-miniapp</artifactId><version>4.3.0</version>
</dependency>
修改配置文件
wx:# 微信小程序appidapp-id: wxcbxxxxxxxxxxxxxxx# 小程序密钥app-secret: 8ccxxxxxxxxxxxxxxxxmsgDataFormat: JSON
配置类Properties
import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** @Author:Ccoo* @Date:2024/3/21 20:40*/
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "wx")
public class WxProperties {/*** 设置微信小程序的appid*/private String appId;/*** 设置微信小程序的Secret*/private String appSecret;/*** 消息格式,XML或者JSON*/private String msgDataFormat;}
配置类Config
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.api.impl.WxMaUserServiceImpl;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Objects;@Slf4j
@Configuration
public class WxMaConfiguration {@Resourceprivate WxProperties wxProperties;@Beanpublic WxMaService wxMaService() {if (Objects.isNull(wxProperties)) {throw new WxRuntimeException("请添加下相关配置, 注意别配错了!");}WxMaService maService = new WxMaServiceImpl();try {WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();config.setAppid(wxProperties.getAppId());config.setSecret(wxProperties.getAppSecret());config.setMsgDataFormat(wxProperties.getMsgDataFormat());HashMap configMap = new HashMap<>();configMap.put(config.getAppid(), config);maService.setMultiConfigs(configMap);} catch (Exception e) {throw new WxRuntimeException("微信小程序相关配置配置失败!");}return maService;}}
Controller控制器代码
import com.itheima.mp.domain.R;
import com.itheima.mp.service.WeChatService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** <p>* 前端控制器* </p>** @author Ccoo* @since 2023-10-01*/
@RestController
@RequestMapping("/wechat")
public class WeChatController {@Autowiredprivate WeChatService wechatService;@ApiOperation(value = "微信小程序授权登录", notes = "微信小程序授权登录")@PostMapping("/wxLogin")public R<?> wxLogin(@ApiParam("loginCode") @RequestParam String code) {return wechatService.wxLogin(code);}}
Service接口层代码
import com.itheima.mp.domain.R;/*** @author Ccoo* 2024/8/23*/
public interface WeChatService {/*** 微信授权登录* @param code 授权码* @return 结果*/R<?> wxLogin(String code);}
ServiceImpl实现层代码
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.stereotype.Service;/*** @author Ccoo* 2024/8/23*/
@Slf4j
@Service
@RequiredArgsConstructor
public class WeChatServiceImpl implements WeChatService {private final WxMaService wxMaService;/*** 微信授权登录** @param code 授权码* @return 结果*/@Overridepublic R<?> wxLogin(String code) {try {WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);String info = JSONUtil.toJsonStr(sessionInfo);log.info("微信授权登录成功:{}", info);return R.ok(info);} catch (WxErrorException e) {log.error("微信授权登录失败:{}", e.getError().getErrorMsg());return R.fail(e.getError().getErrorCode(), e.getError().getErrorMsg());}}
}
统一响应类 R
import java.io.Serializable;/*** 响应信息主体** @author Ccoo*/
public class R<T> implements Serializable
{private static final long serialVersionUID = 1L;/** 成功 */public static final int SUCCESS = 200;/** 失败 */public static final int FAIL = 500;private int code;private String msg;private T data;public static <T> R<T> ok(){return restResult(null, SUCCESS, "操作成功");}public static <T> R<T> ok(String msg){return restResult(null, SUCCESS, msg);}public static <T> R<T> ok(T data){return restResult(data, SUCCESS, "操作成功");}public static <T> R<T> ok(T data, String msg){return restResult(data, SUCCESS, msg);}public static <T> R<T> fail(){return restResult(null, FAIL, "操作失败");}public static <T> R<T> fail(String msg){return restResult(null, FAIL, msg);}public static <T> R<T> fail(T data){return restResult(data, FAIL, "操作失败");}public static <T> R<T> fail(T data, String msg){return restResult(data, FAIL, msg);}public static <T> R<T> fail(int code, String msg){return restResult(null, code, msg);}private static <T> R<T> restResult(T data, int code, String msg){R<T> apiResult = new R<>();apiResult.setCode(code);apiResult.setData(data);apiResult.setMsg(msg);return apiResult;}public int getCode(){return code;}public void setCode(int code){this.code = code;}public String getMsg(){return msg;}public void setMsg(String msg){this.msg = msg;}public T getData(){return data;}public void setData(T data){this.data = data;}
}
具体授权登录代码
微信小程序授权登录思路 :
- 先判断Redis中缓存是否命中, 如果命中则直接返回缓存信息
- 如果未命中缓存, 则使用临时校验码code去微信换取openid
- 查询数据库该openid对应用户信息是否存在, 存在则直接返回, 不存在则注册用户信息
- 最后将用户信息存入redis, 并返回相应的token供下次授权登录前判断是否缓存信息
具体实现代码如下
@ApiOperation("微信授权登录")
@PostMapping("/customer_login")
public R<SysWxUser> customerLogin(@RequestBody WXAuth wxAuth) {return weixinService.customerLogin(wxAuth);
}
R<SysWxUser> customerLogin(WXAuth wxAuth);
/*** 授权登录** @param wxAuth 授权信息* @return 结果*/
@Override
public R<SysWxUser> customerLogin(WXAuth wxAuth) {String code = wxAuth.getCode();String iv = wxAuth.getIv();String token = wxAuth.getToken();String encryptedData = wxAuth.getEncryptedData();if(StrUtil.isBlank(code) && StrUtil.isBlank(token)){return R.fail(MessageConstants.PARAMS_ERROR);}// 判断登录态是否在有效期if(StrUtil.isNotBlank(token)){// token不为空 判断token是否过期String content = redisTemplate.opsForValue().get(RedisKey.WX_SESSION_KEY + token);if(content == null){return R.fail(MessageConstants.TOKEN_NOT_EXIST);}// 查询token对应用户信息SysWxUser info = JSONUtil.toBean(content, SysWxUser.class);// 刷新token有效时间redisTemplate.opsForValue().set(RedisKey.WX_SESSION_KEY + token, content, 3600, TimeUnit.SECONDS);return R.ok(info);}SysWxUser wxUser = null;WxMaJscode2SessionResult sessionInfo = null;try {sessionInfo = wxMaService.getUserService().getSessionInfo(code);WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionInfo.getSessionKey(), encryptedData, iv);String openid = sessionInfo.getOpenid();// 使用微信openId查询是否有此用户(WxUser)wxUser = lambdaQuery().eq(SysWxUser::getOpenid, openid).one();// 不存在 构建用户信息进行注册if(wxUser == null) {wxUser = new SysWxUser();wxUser.setOpenid(openid);wxUser.setWxName(Constants.WX_PREFIX + RandomUtil.randomString(6));wxUser.setAvatarUrl(userInfo.getAvatarUrl());int insert = wxUserMapper.insert(wxUser);if (insert == 0) {return R.fail(MessageConstants.USER_BIND_ERROR);}}} catch (Exception e) {e.printStackTrace();log.error(MessageConstants.SYSTEM_ERROR);}String sessionKey = sessionInfo.getSessionKey();String cacheKey = RedisKey.WX_SESSION_KEY + sessionKey;// 将 openid / sessionKey 存入redisredisTemplate.opsForValue().set(cacheKey, JSONUtil.toJsonStr(wxUser), 3600, TimeUnit.SECONDS);return R.ok(wxUser,Constants.TOKEN_PRE + sessionKey);
}