# weixin-java-miniapp 小程序登录
github 地址:https://github.com/Wechat-Group/WxJava
# 引入依赖:
<dependency> | |
<groupId>com.github.binarywang</groupId> | |
<artifactId>weixin-java-miniapp</artifactId> | |
<version>4.6.0</version> | |
</dependency> |
# 在 application.yml 配置小程序登录信息
wxmini: | |
appid: wx640242aed1878581 | |
secret: 9a8b5c2e0d07d79f13cae8a2b5c61e15 | |
msgDataFormat: JSON | |
tokensecret: oiasj123j23 |
# 创建 Config
目标:
- 配置 appid
- 配置 secret
- 配置 msgDataFormat
- 配置其它信息
package org.dromara.dkxmodules.xcx.config; | |
import cn.binarywang.wx.miniapp.api.WxMaService; | |
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; | |
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; | |
import lombok.Data; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | |
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
@Data | |
@Configuration | |
@ConditionalOnClass(WxMaService.class) | |
public class WxConfig | |
{ | |
@Value(value = "${wxmini.appid}") | |
private String appid; | |
@Value(value = "${wxmini.secret}") | |
private String secret; | |
// @Value(value = "") | |
private String token; | |
// @Value(value = "") | |
private String aesKey; | |
@Value(value = "${wxmini.msgDataFormat}") | |
private String msgDataFormat; | |
@Bean | |
@ConditionalOnMissingBean(WxMaService.class) | |
public WxMaService getWxMaService() | |
{ | |
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); | |
config.setAppid(appid); | |
config.setSecret(secret); | |
config.setMsgDataFormat(msgDataFormat); | |
WxMaServiceImpl wxMaService = new WxMaServiceImpl(); | |
wxMaService.setWxMaConfig(config); | |
return wxMaService; | |
} | |
} |
# 创建实体类和参数类
UserEntity
@Data | |
@TableName("tab_user") | |
@Builder | |
public class UserEntity implements Serializable { | |
private static final long serialVersionUID = 1L; | |
/** | |
* 用户 id | |
*/ | |
@TableId | |
private Long id; | |
/** | |
* 用户名 | |
*/ | |
private String nickname; | |
/** | |
* 用户性别 | |
*/ | |
private String sex; | |
/** | |
* 用户头像 | |
*/ | |
private String head; | |
/** | |
* 用户生日 | |
*/ | |
private String birthday; | |
/** | |
* 微信登录标识 id | |
*/ | |
private String openId; | |
/** | |
* 微信号码 | |
*/ | |
private String wxUnionId; | |
/** | |
* 手机号码 | |
*/ | |
private String phone; | |
/** | |
* 用户状态 (0: 启用,1: 未启用) | |
*/ | |
private String state; | |
/** | |
* 用户地区 | |
*/ | |
private String address; | |
/** | |
* 用户创建时间 | |
*/ | |
private Date createTime; | |
/** | |
* 用户删除时间 | |
*/ | |
private Date deleteTime; | |
/** | |
* 逻辑删除 | |
*/ | |
@TableLogic | |
private Integer isDel; | |
} |
UserInfo
import lombok.Data; | |
import lombok.NoArgsConstructor; | |
import java.math.BigInteger; | |
@Data | |
@NoArgsConstructor | |
public class UserInfo | |
{ | |
private BigInteger id; | |
/** | |
* 用户名 | |
*/ | |
private String nickname; | |
/** | |
* 用户性别 | |
*/ | |
private String gender; | |
/** | |
* 用户头像 | |
*/ | |
private String avatarUrl; | |
/** | |
* 用户生日 | |
*/ | |
private String birthday; | |
/** | |
* 微信登录标识 id | |
*/ | |
private String openId; | |
/** | |
* 微信号码 | |
*/ | |
private String wxUnionId; | |
/** | |
* 手机号码 | |
*/ | |
private String phone; | |
/** | |
* 市 | |
*/ | |
private String city; | |
/** | |
* 国家 | |
*/ | |
private String country; | |
/** | |
* 语言 | |
*/ | |
private String language; | |
/** | |
* 省 | |
*/ | |
private String province; | |
} |
Params
@Data | |
@NoArgsConstructor | |
public class Params | |
{ | |
private String encryptedData; | |
private String iv; | |
private String sessionId; | |
private String appId; | |
} |
# 创建 Mapper 层
@Mapper | |
public interface XcxUserMapper extends BaseMapper<UserEntity> | |
{ | |
} |
# 创建 Service 和实现类层
service
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |
import com.baomidou.mybatisplus.extension.service.IService; | |
import org.dromara.common.core.domain.R; | |
import org.dromara.dkxmodules.xcx.entity.UserEntity; | |
public interface WxLoginService extends IService<UserEntity> | |
{ | |
R authLogin(WxMaUserInfo wxMaUserInfo, String phoneNumber, String sessionKey); | |
} |
serviceimpl
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |
import cn.hutool.core.bean.BeanUtil; | |
import cn.hutool.jwt.JWTUtil; | |
import com.alibaba.fastjson.JSON; | |
import com.alibaba.fastjson.JSONObject; | |
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |
import lombok.NonNull; | |
import lombok.RequiredArgsConstructor; | |
import org.dromara.common.core.domain.R; | |
import org.dromara.dkxmodules.xcx.entity.UserEntity; | |
import org.dromara.dkxmodules.xcx.entity.UserInfo; | |
import org.dromara.dkxmodules.xcx.mapper.XcxUserMapper; | |
import org.dromara.dkxmodules.xcx.service.WxLoginService; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.data.redis.core.StringRedisTemplate; | |
import org.springframework.stereotype.Service; | |
import java.math.BigInteger; | |
import java.util.HashMap; | |
import java.util.UUID; | |
import java.util.concurrent.TimeUnit; | |
@Service(value = "wxLoginService") | |
@RequiredArgsConstructor | |
public class WxLoginServiceImpl extends ServiceImpl<XcxUserMapper, UserEntity> implements WxLoginService | |
{ | |
@Value(value = "${tokensecret}") | |
private String tokenSecret; | |
@NonNull | |
private StringRedisTemplate redisTemplate; | |
@Override | |
public R authLogin(WxMaUserInfo wxMaUserInfo, String phoneNumber, String sessionKey) | |
{ | |
UserInfo userInfo = new UserInfo(); | |
BeanUtil.copyProperties(wxMaUserInfo, userInfo); | |
userInfo.setPhone(phoneNumber); | |
String s = redisTemplate.opsForValue().get("xcxLogin" + sessionKey); | |
JSONObject jsonObject = JSON.parseObject(s); | |
String openId = (String) jsonObject.get("openId"); | |
UserEntity userEntity = baseMapper.selectOne(Wrappers.<UserEntity>lambdaQuery() | |
.eq(UserEntity::getOpenId, openId)); | |
if (userEntity == null) | |
{ | |
return getRegister(userInfo); | |
} | |
else | |
{ | |
return getLogin(userInfo); | |
} | |
} | |
private R getLogin(UserInfo userInfo) | |
{ | |
HashMap<String, Object> map = new HashMap<>(); | |
map.put("id", userInfo.getId()); | |
map.put("nickname", userInfo.getNickname()); | |
map.put("phone", userInfo.getPhone()); | |
String token = JWTUtil.createToken(map, tokenSecret.getBytes()); | |
map.put("token", token); | |
UUID uuid = UUID.randomUUID(); | |
redisTemplate.opsForValue().set("token_" + uuid, JSON.toJSONString(map), 7, TimeUnit.DAYS); | |
return R.ok("token: " + token); | |
} | |
private R getRegister(UserInfo userInfo) | |
{ | |
UserEntity userEntity = UserEntity.builder() | |
.nickname(userInfo.getNickname()) | |
.sex(userInfo.getGender()) | |
.openId(userInfo.getOpenId()) | |
.head(userInfo.getAvatarUrl()) | |
.phone(userInfo.getPhone()) | |
.address(/*userInfo.getCountry() + "/" + userInfo.getProvince() + "/" +*/ userInfo.getCity()) | |
.build(); | |
this.baseMapper.insert(userEntity); | |
userInfo.setId(BigInteger.valueOf(userEntity.getId())); | |
return this.getLogin(userInfo); | |
} | |
} |
# 创建 controller 层
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | |
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |
import io.netty.util.internal.StringUtil; | |
import lombok.AllArgsConstructor; | |
import me.chanjar.weixin.common.error.WxErrorException; | |
import org.dromara.common.core.domain.R; | |
import org.dromara.dkxmodules.xcx.config.WxConfig; | |
import org.dromara.dkxmodules.xcx.entity.dto.Params; | |
import org.dromara.dkxmodules.xcx.service.WxLoginService; | |
import org.springframework.data.redis.core.StringRedisTemplate; | |
import org.springframework.web.bind.annotation.*; | |
import shade.powerjob.com.alibaba.fastjson.JSON; | |
import java.util.HashMap; | |
import java.util.concurrent.TimeUnit; | |
@RestController | |
@RequestMapping("/wxLoginController") | |
@AllArgsConstructor | |
public class WxLoginController | |
{ | |
private WxLoginService wxLoginService; | |
private WxConfig wxConfig; | |
private StringRedisTemplate redisTemplate; | |
@GetMapping("/wxLogin") | |
public Object wxLogin(String code) | |
{ | |
HashMap<String, Object> map = new HashMap<>(); | |
String sessionKey = null; | |
String openid = null; | |
try | |
{ | |
// 通过 code 获取 openid 和 sessionkey | |
WxMaJscode2SessionResult sessionInfo = wxConfig.getWxMaService().getUserService().getSessionInfo(code); | |
openid = sessionInfo.getOpenid(); | |
sessionKey = sessionInfo.getSessionKey(); | |
} catch (WxErrorException e) | |
{ | |
throw new RuntimeException(e); | |
} | |
if (StringUtil.isNullOrEmpty(sessionKey) || StringUtil.isNullOrEmpty(openid)) | |
{ | |
return R.fail(); | |
} | |
map.put("openId", openid); | |
map.put("sessionKey", sessionKey); | |
String toOpenId = JSON.toJSONString(map); | |
redisTemplate.opsForValue().set("xcxLogin" + sessionKey, toOpenId, 3000, TimeUnit.MINUTES); | |
return map; | |
} | |
@PostMapping("/authLogin") | |
public R authLogin(@RequestBody Params params, String sessionKey) | |
{ | |
if (wxConfig.getWxMaService().switchover(params.getAppId())) | |
{ | |
throw new RuntimeException(String.format("未找到对应appid=[%s]的配置,请核实!", params.getAppId())); | |
} | |
// 通过 解密 加密字符串获取用户信息 | |
WxMaUserInfo userInfo = wxConfig.getWxMaService().getUserService() | |
.getUserInfo(params.getSessionId(), params.getEncryptedData(), params.getIv()); | |
// 通过 解密 加密字符串获取用户手机号 | |
WxMaPhoneNumberInfo phoneNoInfo = wxConfig.getWxMaService().getUserService() | |
.getPhoneNoInfo(params.getSessionId(), params.getEncryptedData(), params.getIv()); | |
String phoneNumber = phoneNoInfo.getPhoneNumber(); | |
return wxLoginService.authLogin(userInfo, phoneNumber, sessionKey); | |
} | |
} |