本系列文章记录“智能提醒助理”wx公众号 建设历程。
本文介绍,如何让用户进入小程序之后就锁定用户,开始使用功能,去掉繁琐的登录认证流程。
一、需求出发点
用户进入小程序之后就开始使用功能,去掉繁琐的登录流程。拿来即用。
二、实现路径分析
方案一、小程序初始化之后,获取code,在服务端通过code获取openid作为用户标识。
方案二、在用户触发请求时,进行拦截,判断是否没有登录,则获取code,在服务端通过code获取openid作为用户标识。
三、最终方案
选择 方案二
四、实施路径
1、请求拦截设计
// 引入 uni-ajax 模块
import ajax from '@/uni_modules/u-ajax'
import {getAccessToken, xcxlogin} from '@/common/auth'
import {toast} from '@/common/common'
import config from '@/config'
import store from '@/store/index'
const baseUrl = config.baseUrl + config.baseApi;
const appId = config.appId;
// 创建请求实例
const instance = ajax.create({
// 初始配置
baseURL: baseUrl,
// needAuth: true
})
// 添加请求拦截器
instance.interceptors.request.use(async config => {
let token = getAccessToken();
console.log("请求拦截器 config:",token, config.url)
console.log("请求拦截器 member:",store.state.member.id)
if (token ==="" && config.url !=="/auth/loginByXcx" ) {
if(uni.getStorageSync("xcxlogin")==="1"){
console.log("重复请求")
return config;
}
uni.setStorageSync("xcxlogin","1")
await xcxlogin();//登录
uni.setStorageSync("xcxlogin","0")
token = getAccessToken();
}
config.header['token'] = token;
config.header['appId'] = appId;
return config;
})
// 添加响应拦截器
instance.interceptors.response.use(
response => {
console.log("响应拦截器:response.data.msg==>",response)
console.log("响应拦截器:response.data.msg==>",response.data.msg)
let statusCode = response.statusCode;
if (statusCode === 200) {
let code = response.data.code || 0;
switch (code) {
case '400':
uni.showToast({title: response.data.msg, icon: 'none', duration: 2000})
break;
case '401':
// token 自动续期
xcxlogin();
//toast(response.data.msg)
break;
case '500':
uni.showToast({title: response.data.msg, icon: 'none', duration: 2000})
break;
}
} else {
toast("请求失败")
}
// 对响应数据做些什么
return response.data
},
error => {
// 对响应错误做些什么
return Promise.reject(error)
}
)
// 导出 create 创建后的实例
export default instance
2、用户登录设计
import store from '@/store/index'
import request from '@/api/config/request';
const AccessTokenKey = 'ACCESS_TOKEN'
const RefreshTokenKey = 'REFRESH_TOKEN'
// ========== Token 相关 ==========
export function getAccessToken() {
return uni.getStorageSync(AccessTokenKey)
}
export function getRefreshToken() {
return uni.getStorageSync(RefreshTokenKey)
}
export function setToken(token) {
uni.setStorageSync(AccessTokenKey, token)
uni.setStorageSync(RefreshTokenKey, token)
}
export function removeToken() {
uni.removeStorageSync(AccessTokenKey)
uni.removeStorageSync(RefreshTokenKey)
}
export async function loginByXcx(code) {
let res = await request.post({
url: '/auth/loginByXcx',
data: {'code': code}
});
const loginRes = res.data;
setToken(loginRes["token"]);
// 设置用户信息
store.commit('SET_MEMBER', loginRes["userInfo"]);
return res;
}
// 用户静默登录
export async function xcxlogin() {
let res = await uni.login({
provider: 'weixin', // 使用微信登录
});
return await loginByXcx(res[1]["code"])
}
3、服务端静默登录设计
/** 微信小程序登录 */
@RequestMapping("loginByXcx")
public Object loginByXcx(@RequestBody String data) throws IOException {
StopWatch stopWatch = new StopWatch();
stopWatch.start("获取 登陆凭证");
// 获取 登陆凭证code
log.info("loginByXcx,code:{}",data);
String loginIp = IpUtils.getIpAddr(request);
// ip黑名单 校验
// String login_black_ip_list = paramDictService.getKey(CacheConstant.XCX_LOGIN_BLACK_IP_LIST);
// if (loginIp.matches(login_black_ip_list)) {
// return R.failure("登录失败,疑似机器人注册!");
// }
// 获取 登陆凭证code
HashMap<String, Object> jsonParam = JsonUtil.toObj(data, HashMap.class);
String code = "";
if (!StringUtils.isEmpty(jsonParam.get("code"))) {
code = (String) jsonParam.get("code");
}
if(StringUtils.isEmpty(code)){
return R.failure("登录失败,code缺失");
}
stopWatch.stop();
stopWatch.start("jscode2session 获取openid unionid");
// jscode2session 获取openid unionid
String resData = null;
try {
resData = wechatUtil.code2session(code);
} catch (Exception e) {
return R.failure("登录失败,获取openid失败");
}
stopWatch.stop();
stopWatch.start("jscode2session 解析响应结果");
HashMap<String, String> sessionData = JsonUtil.toObj(resData, HashMap.class);
log.info("loginByXcx,sessionData:{}",sessionData);
String xcxOpenId = sessionData.get("openid");
String unionid = sessionData.get("unionid");
// 解析响应结果
if (StringUtils.isEmpty(xcxOpenId)) {
return R.failure("登录失败");
}
stopWatch.stop();
stopWatch.start("静默注册");
// 静默注册
UserInfo userInfo = userInfoService.silenceRegister(unionid,loginIp,xcxOpenId,"");
log.info("loginByXcx,静默注册完成:{}",userInfo.getId());
stopWatch.stop();
stopWatch.start("创建token");
// 创建token
Map<String, Object> tokenMap = tokenService.createToken(userInfo.getId(), userInfo.getNickName());
String token = MapUtils.getString(tokenMap, "token");
log.info("创建token:" + token);
stopWatch.stop();
stopWatch.start("获取整合后的用户信息");
// 获取整合后的用户信息
UserInfoVo resUserInfo = userInfoService.getUserInfo(userInfo.getId());
stopWatch.stop();
log.info("stopWatch 总耗时:{}",stopWatch.getTotalTimeMillis());
log.info("stopWatch 详细信息:{}",stopWatch.prettyPrint());
HashMap resultObj = new HashMap();
resultObj.put("token", token);
resultObj.put("userInfo", resUserInfo);
return R.success(resultObj);
}
五、总结
静默登录的好处在于,使用既注册,减少用户操作、降低犹豫期,先识别用户,后续再引导用户完善资料,最小化设计。比如:设置头像、昵称等用户信息。
从使用上来讲,只要有用户标识,就可以使用功能做交易了,如果用户需要展示个性化信息,可以再设置。
后续 在设计分享推荐功能的时候,就可以让用户分享即可关联推荐人。
欢迎大家来体验这款新上线的 智能提醒工具,感兴趣的在公众号 留言交流哈。