微信公众号免密登录
1. 微信公众号官网文档
2. 开发
2.1 后端微信配置
2.1.1 配置微信
#微信公众号配置
wexin:
#开发者ID
appid: 保密
#开发者密码
secret: 保密
#获取token的请求地址
getTokenUrl: https://api.weixin.qq.com/sns/oauth2/access_token
2.1.2 微信公众号接口参数配置
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chery.common.constant.Constants;
import com.chery.common.utils.http.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @description: 调用微信公众号接口参数配置
* @date 2020-11-27
*/
@Component
@Slf4j
@ConfigurationProperties(prefix = "wexin")
public class EnterpriseWeXin {
/**
* 获取token url
*/
private static String getTokenUrl;
/**
* 开发者ID
*/
private static String appid;
/**
* 开发者密码
*/
private static String secret;
public static String getGetTokenUrl() {
return getTokenUrl;
}
public void setGetTokenUrl(String getTokenUrl) {
EnterpriseWeXin.getTokenUrl = getTokenUrl;
}
public static String getAppid() {
return appid;
}
public void setAppid(String appid) {
EnterpriseWeXin.appid = appid;
}
public static String getSecret() {
return secret;
}
public void setSecret(String secret) {
EnterpriseWeXin.secret = secret;
}
public static String getOpenId(String code) {
String openid = "";
try {
String param = "appid=" + EnterpriseWeXin.getAppid() + "&secret=" + EnterpriseWeXin.getSecret() + "&code=" + code + "&grant_type=authorization_code";
String resStr = HttpUtils.sendGet(EnterpriseWeXin.getGetTokenUrl(), param);
if (StringUtils.isNotEmpty(resStr)) {
JSONObject res = JSON.parseObject(resStr);
if (res != null && res.containsKey(Constants.OPENID)) {
openid = res.getString(Constants.OPENID);
}
}
} catch (Exception e) {
log.error("微信公众号获取openid失败", e.getMessage());
}
return openid;
}
}
2.2 免密登录
2.2.1 Uniapp端默认页面bind.vue
<template>
<view>
</view>
</template>
<script>
export default {
onLoad(option) {
//url提供给微信,然后微信将code传递到该url页面,我这边选的是登录页login.vue
const url = "http%3A%2F%2Fyuhai.com%2Fpages%2Flogin%2Flogin";
//发起微信请求,appid参数在公众号获取;scope有两种模式详情见文档
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
"appid=wx95a639d8025a505a&redirect_uri=" + url + "&scope=snsapi_base&response_type=code&state=STATE#wechat_redirect";
},
methods: {
toLogin() {
uni.reLaunch({
url: '/pages/login/login',
});
}
}
}
</script>
<style>
</style>
踩过的坑:
- 回调的url不要带#号,不然回调页面无法获取code
- 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
什么是urlEncode 对链接进行处理?
打开谷歌浏览器的调试控制台
控制台输入encodeURIComponent(‘url地址’),复制出返回的字符串即可,单引号别复制
2.2.2 Uniapp端回调login登录页实现免密登录
onLoad
onLoad(option) {
//获取回调code
this.loginForm.code = option.code;
//发起code登录
this.wxLoginByCode()
}
methods
wxLoginByCode() {
//发起微信免密登录
this.$u.get('/wechat/login?code=' + this.loginForm.code).then(res => {
if (res.code == 200 && res.token) {
uni.setStorageSync('token', res.token)
this.$u.get('/getInfo').then(res => {
uni.dicParams.user = res.user
uni.setStorageSync('user', res.user)
uni.reLaunch({
url: '/pages/index/index',
});
})
} else {
uni.showToast({
icon: 'none',
title: res.msg,
});
}
}).catch(res => {
uni.showToast({
icon: 'none',
title: res.msg,
});
})
},
2.2.3 后端免密登录接口
/**
* 微信免密登录
*
* @param code
* @param state
* @return
*/
@GetMapping("/wechat/login")
public AjaxResult wechatLogin(@RequestParam("code") String code) {
AjaxResult ajax = AjaxResult.success();
if (StrUtil.isEmpty(code)) {
return AjaxResult.error("code不存在");
}
// 生成令牌
String token = loginService.loginWx(code);
ajax.put(Constants.TOKEN, token);
return ajax;
}
/**
* 微信免密登录
*
* @param code
* @return
*/
public String loginWx(String code) {
String openId = EnterpriseWeXin.getOpenId(code);
redisCache.setCacheObject(CacheConstants.WEI_XIN_CODE_OPEN_ID_KEY + code, openId, 5, TimeUnit.MINUTES);
//处理企业微信登录
LoginUser loginUser = null;
//校验openId
SysUser user = userService.getUserByOpenId(openId);
if (StringUtils.isNotNull(user)) {
loginUser = new LoginUser(user, permissionService.getMenuPermission(user));
recordLoginInfo(loginUser.getUser());
// 生成token
return tokenService.createToken(loginUser);
} else {
throw new ServiceException("未绑定账号,请手动登录!");
}
}
2.2.4 踩过的坑
-
为什么免密登录获取到openid需要缓存在redis五分钟?
官网文档:用户同意授权,获取code
如果不缓存并且用户还没绑定到系统,此时用户手动登录时获取不到openid,导致用户系统账号不会和微信openid绑定 -
开放匿名微信登录接口
.antMatchers("/wechat/login").permitAll()
不要放在
.antMatchers("/login","/wechat/login", "/captchaImage").anonymous()
2.3 手动登录
用户第一次登录系统,未绑定系统账户时使用的功能
实现用户登录和用户绑定业务需求
2.3.1 Uniapp端login登录页实现手动登录
methods
基本不用动,在请求/login请求时添加code: this.loginForm.code
参数
login() {
if (this.loginForm.userName == 0) {
uni.showToast({
icon: 'none',
title: '用户名不能为空'
});
return;
}
if (this.loginForm.password.length == 0) {
uni.showToast({
icon: 'none',
title: '密码不能为空'
});
return;
}
this.$u.post('/login', {
username: this.loginForm.userName,
password: this.loginForm.password,
code: this.loginForm.code
}).then(res => {
if (res.code == 200) {
if (this.loginForm.rememberPsw) { //用户勾选“记住密码”
console.log("记住密码")
uni.setStorageSync('userName', this.loginForm.userName);
uni.setStorageSync('password', this.loginForm.password);
} else { //用户没有勾选“记住密码”
console.log("没有记住密码")
uni.removeStorageSync('userName');
uni.removeStorageSync('password');
this.loginForm.userName = "";
this.loginForm.password = "";
}
uni.setStorageSync('token', res.token)
this.$u.get('/getInfo').then(res => {
uni.dicParams.user = res.user
uni.setStorageSync('user', res.user)
uni.reLaunch({
url: '/pages/index/index',
});
})
} else {
uni.showToast({
icon: 'none',
title: res.msg,
});
}
}).catch(res => {
uni.showToast({
icon: 'none',
title: res.msg,
});
})
}
2.3.2 后端login接口
/**
* 登录方法
*
* @param loginBody 登录信息
* @return 结果
*/
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody) {
Long userId = null;
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getUuid());
//账号绑定微信openid
if (StringUtils.isNotEmpty(token) && StringUtils.isNotEmpty(loginBody.getCode())) {
String code = loginBody.getCode();
String userName = loginBody.getUsername();
CompletableFuture.runAsync(() -> {
//无法请求微信获取openId,所以从缓存获取
String openId = redisCache.getCacheObject(CacheConstants.WEI_XIN_CODE_OPEN_ID_KEY + code);
if (StringUtils.isEmpty(openId)) {
log.error("用户: {}绑定微信失败!", userName);
} else {
//根据userName更新openId
userService.updateOpenIdByUserName(openId, userName);
log.info("用户:{}绑定微信成功!", userName);
}
}, threadPoolExecutor);
}
ajax.put(Constants.TOKEN, token);
return ajax;
}