引子
感觉微信登录刚开始用的时候真的好绕啊,感觉很多东西自己不太能用的清楚,下面是自己的简易版记录,主要为了做个笔记,如果觉得有些出入可以提出,自己也是一个新人啦。
前端
- 通过调用 wx.login 接口来进行code的获取,从而得到确定用户唯一身份的openId和会话密钥sessionkey;
- 通过获取微信手机号来得到密钥encryptedData以及矢量iv,通过wx.getUserInfo得到用户信息(其实这个得到用户信息没有太大的必要,因为他现在经过修改得到的用户名称和头像都不是微信的了,都是默认的,但我觉得把这个默认的先存进去做我一个默认设定的也行)
<button wx:if="{{!isBindPhone}}" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">获取手机号</button>
import util from '../../utils/util';
onGetPhoneNumber(e) {
//授权
if (e.detail.errMsg == 'getPhoneNumber:fail user deny') {
wx.showToast({
title: "必须绑定手机号才可登录",
icon: 'none'
})
return;
}
let encryptedData = e.detail.encryptedData;
let iv = e.detail.iv;
var that = this;
wx.getSetting({
success(e){
wx.getUserInfo({
success: function(res) {
// console.log(res);
//将其数据通过wx.request传到后端,
//其中loginByWeixin是自己写的方法名
util.loginByWeixin(res.userInfo,encryptedData,iv);
}
})
}
})
后端
作为一个后端小白,真的是从头开始走的…┭┮﹏┭┮
- 依赖
<!--用的是binarywang的接口--> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-miniapp</artifactId> <version>4.5.0</version> </dependency> <!--redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency> <!--序列化,反序列化--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.38</version> </dependency>
- 这里加个微信接口的配置
- WxConfig
@Configuration
@EnableConfigurationProperties(WxProperties.class)
public class WxConfig {
@Autowired
private WxProperties wxProperties;
public WxConfig(WxProperties wxProperties){
this.wxProperties=wxProperties;
}
@Bean
public WxMaService wxMaService() {
List<WxProperties.Config> configs = this.wxProperties.getConfigs();
if (configs == null) {
System.out.println("没有微信小程序配置啊");
}
WxMaServiceImpl maService = new WxMaServiceImpl();
Map<String, WxMaDefaultConfigImpl> multiConfigs = configs.stream()
.map(cof -> {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(cof.getAppid());
config.setSecret(cof.getSecret());
config.setToken(cof.getToken());
config.setAesKey(cof.getAesKey());
config.setMsgDataFormat(cof.getMsgDataFormat());
return config;
})
.collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, Function.identity(), (o, n) -> o));
maService.setMultiConfigs(multiConfigs);
return maService;
}
}
- WxProperties
@Data
@Configuration
//wechat-setting是在application.yml中配置的名称
@ConfigurationProperties(prefix = "wechat-setting")
public class WxProperties {
private List<Config> configs;
@Data
public static class Config {
/**
* 设置微信小程序的appid
*/
private String appid;
/**
* 设置微信小程序的Secret
*/
private String secret;
/**
* 设置微信小程序消息服务器配置的token
*/
private String token;
/**
* 设置微信小程序消息服务器配置的EncodingAESKey
*/
private String aesKey;
/**
* 消息格式,XML或者JSON
*/
private String msgDataFormat;
}
}
- application.yml
# 注意写法
wechat-setting:
configs:
- appid:
secret:
- jedis 配置
- RedisConfig
public class RedisConfig {
@Value("${spring.redis.host}")
private String ADDR;
//端口号
@Value("${spring.redis.port}")
private int PORT ;
//密码
@Value("${spring.redis.auth}")
private String AUTH ;
//连接的时间,一旦超时就报错
@Value("${spring.redis.timeout}")
private int TIMEOUT ;
//可用连接实例的最大数目,默认值为8;
//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
private int MAX_ACTIVE = 1024;
//控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值8。
private int MAX_IDLE = 200;
//等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
private int MAX_WAIT = 10000;
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
private boolean TEST_ON_BORROW = true;
private JedisPool pool = null;
//连接池
@Bean
public JedisPool getJedisPool()
{
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
Integer port = new Integer(PORT);
//pool = new JedisPool(config, ADDR, port, TIMEOUT);
pool = new JedisPool(config, ADDR, port, TIMEOUT, AUTH);
try {
pool.getResource();
} catch (JedisConnectionException e) {
System.out.println("redis没有设置密码");
pool = new JedisPool(config, ADDR, port, TIMEOUT);
e.printStackTrace();
}
return pool;
} catch (Exception e) {
e.printStackTrace();
}
return pool;
}
/**
* 返还到连接池
* @param redis
*/
public void returnResource(Jedis redis) {
if (redis != null) {
redis.close();
}
}
public String get(String key) {
Jedis jedis = null;
String value = null;
try {
jedis = pool.getResource();
//选择第几个数据库
jedis.select(0);
value = jedis.get(key);
} catch (Exception e) {
e.printStackTrace();
} finally {
returnResource(jedis);
}
return value;
}
public String set(String key, String value) {
Jedis jedis = null;
try {
// 选择数据库
jedis = pool.getResource();jedis.select(0);
return jedis.set(key, value);
} catch (Exception e) {
e.printStackTrace();
} finally {
returnResource(jedis);
}
return "0";
}
/**
*
* @param key
* @param value
* @param seconds 单位:秒
* @return 成功返回OK 失败和异常返回null
*/
public String setex(String key, String value, int seconds) {
Jedis jedis = null;
String res = null;
try {
jedis = pool.getResource();
jedis.select(0);
res = jedis.setex(key, seconds, value);
} catch (Exception e) {
e.printStackTrace();
} finally {
returnResource(jedis);
}
return res;
}
- application.yml
sping:
redis:
# 默认
host: 127.0.0.1
# 默认
port: 6379
timeout: 10000
# 密码可以自己设定
auth: 12345
- 最后使用
@RestController
@RequestMapping("/wechat")
public class WeChatController {
private Logger logger = LoggerFactory.getLogger(WeChatController.class);
@Resource
private WxMaService wxMaService;
@Resource
private IUserService userService;
@Resource
private RedisConfig redisConfig;
// @Resource
// private ReturnForm returnForm;
//获取openId
@RequestMapping("/login")
public Object getSession(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) {
//ifcode为空的话则报错给前端
if (Strings.isNullOrEmpty(wxLoginInfo.getCode())) {
return Result.fail("code参数错误");
}
String code = wxLoginInfo.getCode();
WxUserInfo userInfo = wxLoginInfo.getUserInfo();
if(code==null || userInfo == null){
return Result.fail("code参数错误或userInfo未能获取");
}
String sessionKey = null;
String openId = null;
String unionId = null;
try{
WxMaJscode2SessionResult res = this.wxMaService.getUserService().getSessionInfo(code);
sessionKey = res.getSessionKey();
openId = res.getOpenid();
unionId = res.getUnionid();
System.out.println("sessionKey: "+sessionKey);
System.out.println("openId:"+openId);
System.out.println("unionId:"+unionId);
}catch(Exception e){
logger.error("login_by_wechat error:{}",e);
}
if(sessionKey==null || openId ==null){
return Result.fail("sessionKey或openId为空");
}
//得到了openId
User user=new User();
user.setOpen_id(openId);
Map<String,Object> data = userService.login(user);
ReturnForm returnForm=new ReturnForm();
//则没有该用户,需要创建该用户
if(data==null){
//则获取手机号
WxMaPhoneNumberInfo phoneInfo = wxMaService.getUserService().getPhoneNoInfo(sessionKey, wxLoginInfo.getEncryptedData(), wxLoginInfo.getIv());
String phoneNumber = phoneInfo.getPhoneNumber();
user.setTel(phoneNumber);
user.setAvatar(wxLoginInfo.getUserInfo().getAvatarUrl());
user.setName(wxLoginInfo.getUserInfo().getNickName());
userService.createUser(user);
String key = "user:" + UUID.randomUUID();
//存入redis
redisConfig.setObject(key,user);
returnForm.setToken(key);
returnForm.setUser(user);
return Result.success(returnForm,"创建成功");
}else{
returnForm.setToken(data.get("token").toString());
returnForm.setUser((User)data.get("userInfo"));
return Result.success(returnForm,"查找到相关用户");
}
}
- service
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
/*
* 通过前端获取code信息,再通过微信小程序提供的api解读code,得到用户信息和openid等信息
* 获取用户openid进行查询,是否存在改任
* */
@Resource
private RedisConfig redisConfig;
@Override
public Map<String, Object> login(User user) {
// 根据用户名和密码去查询(openid去查询)
LambdaQueryWrapper<User> wrapper= new LambdaQueryWrapper<>();
wrapper.eq(User::getOpen_id,user.getOpen_id());
User loginUser = this.baseMapper.selectOne(wrapper);
//如果结果不为空,则生成token,并将用户信息存入redis中
if(loginUser != null){
//暂时用UUID,终极方案是jwt
String key = "user:" + UUID.randomUUID();
//存入redis
redisConfig.setex(key,JSON.toJSONString(loginUser),36000);
//返回数据
Map<String,Object> data = new HashMap<>();
data.put("token",key);
data.put("userInfo",loginUser);
return data;
}
return null;
}
/**
*
* @param user
* @return
* 1 表示手机号为空,
* 2 表示创建成功
* 3 表示创建失败
*/
@Override
public Boolean createUser(User user){
return this.saveOrUpdate(user);
}
}
最后,仅供参考了,
日常打气,加油努力!