|
|
@@ -0,0 +1,100 @@
|
|
|
+'use strict';
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * @author Lan
|
|
|
+ * @date 2025/12/22
|
|
|
+ * @version
|
|
|
+ */
|
|
|
+
|
|
|
+const crypto = require('crypto');
|
|
|
+const jwt = require('jsonwebtoken');
|
|
|
+const axios = require('axios');
|
|
|
+const weappConfig = require('../const/weapp');
|
|
|
+
|
|
|
+module.exports = app => {
|
|
|
+ class Weappchat extends app.BaseService {
|
|
|
+ async login(data) {
|
|
|
+ const { code, account, password } = data;
|
|
|
+ const projectData = await this.ctx.service.project.getProjectByCode(
|
|
|
+ code.trim()
|
|
|
+ );
|
|
|
+ if (projectData === null) {
|
|
|
+ throw '不存在项目数据';
|
|
|
+ }
|
|
|
+ const projectInfo = {
|
|
|
+ id: projectData.id,
|
|
|
+ name: projectData.name,
|
|
|
+ code: projectData.code,
|
|
|
+ userAccount: projectData.user_account,
|
|
|
+ };
|
|
|
+ const projectService = this.ctx.service.projectAccount;
|
|
|
+ const accountData = await projectService.db.get(projectService.tableName, {
|
|
|
+ account: data.account,
|
|
|
+ projectId: projectInfo.id,
|
|
|
+ enable: 1,
|
|
|
+ });
|
|
|
+ if (!accountData) {
|
|
|
+ throw '账号不存在或未启用';
|
|
|
+ }
|
|
|
+ // const encryptPassword = crypto.createHmac('sha1', data.account.trim()).update('!*#)385'+).digest().toString('base64');
|
|
|
+ }
|
|
|
+ // 微信小程序登录
|
|
|
+ async weappLogin(code) {
|
|
|
+ try {
|
|
|
+ const wxRes = await axios.get('https://api.weixin.qq.com/sns/jscode2session', {
|
|
|
+ params: {
|
|
|
+ appid: weappConfig.AppID,
|
|
|
+ secret: weappConfig.AppSecret,
|
|
|
+ js_code: code,
|
|
|
+ grant_type: 'authorization_code',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const { openid, session_key, errcode } = wxRes.data;
|
|
|
+ if (errcode || !openid) throw '微信登录失败';
|
|
|
+ // 2. 生成 access_token + refresh_token
|
|
|
+ const accessToken = app.jwt.sign(
|
|
|
+ { openid, type: 'access' },
|
|
|
+ weappConfig.jwtSecret,
|
|
|
+ { expiresIn: weappConfig.accessTokenExpiresIn }
|
|
|
+ );
|
|
|
+
|
|
|
+ const refreshToken = app.jwt.sign(
|
|
|
+ { openid, type: 'refresh' },
|
|
|
+ weappConfig.jwtSecret,
|
|
|
+ { expiresIn: weappConfig.refreshTokenExpiresIn }
|
|
|
+ );
|
|
|
+ // 3. 存入 Redis:openid 对应最新 refreshToken
|
|
|
+ const key = `user:${openid}:refresh_token`;
|
|
|
+ await app.redis.set(key, refreshToken, 'EX', weappConfig.redisExpire);
|
|
|
+ return { accessToken, refreshToken };
|
|
|
+ } catch (error) {
|
|
|
+ throw '微信登录失败';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 刷新 access token
|
|
|
+ * @param {string} refreshToken - 刷新 token
|
|
|
+ * @return {string} newAccessToken
|
|
|
+ */
|
|
|
+ async refresh(refreshToken) {
|
|
|
+ // 1. 验证 refreshToken
|
|
|
+ const decoded = jwt.verify(refreshToken, weappConfig.jwtSecret);
|
|
|
+ if (decoded.type !== 'refresh') {
|
|
|
+ throw 'token 类型错误';
|
|
|
+ }
|
|
|
+ const { openid } = decoded;
|
|
|
+ // 2. 校验 Redis 中是否存在(防止篡改/过期/被踢)
|
|
|
+ const redisKey = `user:${openid}:refresh_token`;
|
|
|
+ const redisRefreshToken = await app.redis.get(redisKey);
|
|
|
+ if (!redisRefreshToken || redisRefreshToken !== refreshToken) {
|
|
|
+ throw 'refresh_token 无效或已过期';
|
|
|
+ }
|
|
|
+ const newAccessToken = app.jwt.sign({ openid, type: 'access' }, weappConfig.jwtSecret, { expiresIn: weappConfig.accessTokenExpiresIn });
|
|
|
+ return newAccessToken;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Weappchat;
|
|
|
+};
|