From fb6a5a785e0c74cbd7b8f94d1caa2136e4bccb89 Mon Sep 17 00:00:00 2001 From: luying Date: Mon, 8 Nov 2021 17:47:30 +0800 Subject: [PATCH] =?UTF-8?q?sdk:=20=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/servers/role/handler/heroHandler.ts | 2 - shared/consts/constModules/httpConst.ts | 13 ++ shared/consts/statusCode.ts | 2 + shared/db/User.ts | 57 ++++++- shared/domain/sdk.ts | 42 ++++++ shared/pubUtils/httpUtil.ts | 142 +++++++++++------- shared/pubUtils/sdkUtil.ts | 34 +++++ shared/pubUtils/util.ts | 6 +- web-server/app/controller/account.ts | 6 + web-server/app/controller/game.ts | 2 +- web-server/app/router.ts | 1 + web-server/app/service/Auth.ts | 44 ++++++ 12 files changed, 291 insertions(+), 60 deletions(-) create mode 100644 shared/domain/sdk.ts create mode 100644 shared/pubUtils/sdkUtil.ts diff --git a/game-server/app/servers/role/handler/heroHandler.ts b/game-server/app/servers/role/handler/heroHandler.ts index 63ac825b8..e4a6aac41 100644 --- a/game-server/app/servers/role/handler/heroHandler.ts +++ b/game-server/app/servers/role/handler/heroHandler.ts @@ -14,8 +14,6 @@ import { pushComposeOrangeHero, pushHeroQualityUpMsg, pushHeroStarMax, pushHeroW import { calculatetopLineup, calEquipSeids } from '../../../pubUtils/playerCe'; import { PvpDefenseModel } from '../../../db/PvpDefense'; import { checkTaskWithHero, checkTask, checkActivityTask } from '../../../services/taskService'; -import { addSkin } from '../../../pubUtils/itemUtils'; -import Skin from '../../../db/Skin'; import { EquipModel, EquipType } from '../../../db/Equip'; import { checkEquipCanPut } from '../../../services/equipService'; import { pick } from 'underscore'; diff --git a/shared/consts/constModules/httpConst.ts b/shared/consts/constModules/httpConst.ts index a4ed1c756..48630a075 100644 --- a/shared/consts/constModules/httpConst.ts +++ b/shared/consts/constModules/httpConst.ts @@ -11,3 +11,16 @@ export enum BANTU_VID_ADDR { REPORT_ONLINE = '/addiction_prevention/report_online' } export const BANTU_VID_APP_KEY = '05c1c495369769e3c5d98426e9c8c2c0'; + +export enum SDK_37_ADDR { + LOGIN = 'https://apimyh5.37.com/index.php?c=sdk-unified&a=validate' +} +export enum SDK_37_CONST { + GAME_ID = 165, // 研发使用的GAME_ID + PID = '37h5', // 37渠道标识 + FX_C_GAME_ID = 21228, // 子游戏id + GAME_KEY = 'sgzzyz', + LOGIN_KEY = '3PerD)H!JdTC_pEUnZl8vXKgasj5;7Q~', // 登录KEY + PAY_KEY = '0;_Zy.qodXCF8NGQmrOYltk%HgfAz*39', // 重置KEY + CHAT_KEY = '3PerD)H!JdTC_pEUnZl8vXKgasj5;7Q~', // 聊天KEY +} \ No newline at end of file diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 02acfe1b2..116af464d 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -30,6 +30,8 @@ export const STATUS = { NAME_HAS_USED: { code: 10015, simStr: '该名字已使用' }, ROLE_HAS_INIT: { code: 10016, simStr: '该角色已初始过' }, ROLE_EXIST: { code: 10017, simStr: '账号已存在' }, + CHANNEL_ERR: { code: 10018, simStr: '无此渠道' }, + VALIDATE_ERR: { code: 10019, simStr: '渠道校验错误' }, // 战斗相关状态 20000 - 29999 // 战斗通用 20000 - 20099 BATTLE_MISS_INFO: { code: 20001, simStr: '缺少关卡信息' }, diff --git a/shared/db/User.ts b/shared/db/User.ts index 7e2f03cd4..341369584 100644 --- a/shared/db/User.ts +++ b/shared/db/User.ts @@ -3,6 +3,7 @@ import { CounterModel } from './Counter'; import BaseModel from './BaseModel'; import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; import { genCode, aesEncryptcfb, aesDecryptcfb } from '../pubUtils/util'; +import { LoginValidateData37, ChannelInfo } from '../domain/sdk'; const bcrypt = require('bcrypt'); const SALT_WORK_FACTOR = 5 @@ -18,6 +19,7 @@ class PlatForm { */ @index({ tel: 1 }) @index({ uid: 1 }) +@index({ channelId: 1 }) export default class User extends BaseModel { @prop({ required: true }) @@ -32,6 +34,15 @@ export default class User extends BaseModel { @prop({ required: true }) token: string; + @prop({ required: true }) + channelType: string; // 渠道类型 + + @prop({ required: true }) + channelId: string; // 渠道类型 + + @prop({ required: true, _id: false }) + channelInfo: LoginValidateData37|{}; // 渠道数据 + @prop({ required: true, set: (val: string) => aesEncryptcfb(val, ENCRYPT_KEY, ENCRYPT_IV), get: (val: string) => aesDecryptcfb(val, ENCRYPT_KEY, ENCRYPT_IV) }) tel: string; // 账号 @@ -44,9 +55,6 @@ export default class User extends BaseModel { @prop({ required: true }) salt: string; - @prop({ required: true, default: '' }) - channelId: string; - // 游客相关 @prop({ required: false, default: false }) isGuest: boolean; // 是否是游客 @@ -123,6 +131,20 @@ export default class User extends BaseModel { return user; } + public static async createUserWithChannel(channelId: string, channelType: string, channelInfO: ChannelInfo, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, ip: string = '') { + const curTime: Date = new Date(); + + const uid = await CounterModel.getNewCounter(COUNTER.UID); + const userCode = genCode(8); + const doc = new UserModel(); + let update = {}; + update = Object.assign(update, doc.toJSON(), { platform, pkgName, serverType, createTime: curTime, uid, userCode, username: `用户${uid}`, token, lastLoginTime: curTime, ip, channelInfO, channelType }); + delete update["device"]; + + const user: UserType = await UserModel.findOneAndUpdate({ channelId }, { $set: update, $addToSet: { device: deviceId } }, { upsert: true, new: true }).lean({ getters: true }); + return user; + } + public static async getLastDeviceGuest(deviceId: string, token: string) { const user: UserType = await UserModel.findOneAndUpdate({ device: { $elemMatch: { $eq: deviceId } }, isGuest: true }, { token }, { new: true }).sort({ createTime: -1 }).lean({ getters: true }); return user; @@ -136,11 +158,23 @@ export default class User extends BaseModel { return user; } + public static async updateTokenByChannel(channelId: string, token: string, deviceId: string, ip: string, channelInfo: ChannelInfo) { + + const curTime: Date = new Date(); + + let user = await UserModel.findOneAndUpdate({ channelId }, { $set: { token, lastLoginTime: curTime, ip, channelInfo }, $addToSet: { device: deviceId } }, { new: true }).lean({ getters: true }); + return user; + } public static async updateGetuiCID(tel: string, cid: string) { let user = await UserModel.findOneAndUpdate({ tel }, { $set: { getuiCID: cid } }, { new: true }).lean({ getters: true }); return user; } + public static async updateGetuiCIDByChannel(channel: string, cid: string) { + let user = await UserModel.findOneAndUpdate({ channel }, { $set: { getuiCID: cid } }, { new: true }).lean({ getters: true }); + return user; + } + public static async createOrUpdate(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, ip: string) { // console.log(tel); @@ -153,6 +187,18 @@ export default class User extends BaseModel { return user; } + public static async createOrUpdateChannelUser(channelId: string, channelType: string, channelInfo: ChannelInfo, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, ip: string) { + // console.log(tel); + + let user: UserType = await UserModel.findUserByChannel(channelId); + if (!user) { + user = await UserModel.createUserWithChannel(channelId, channelType, channelInfo, token, platform, pkgName, serverType, deviceId, ip); + } else { + user = await UserModel.updateTokenByChannel(channelId, token, deviceId, ip, channelInfo); + } + return user; + } + private static async encryptPass(password: string, salt?: string) { if (!salt) { salt = await bcrypt.genSalt(SALT_WORK_FACTOR); @@ -191,6 +237,11 @@ export default class User extends BaseModel { return user; } + public static async findUserByChannel(channelId: string) { + const user: UserType = await UserModel.findOne({ channelId }).select('uid token serverType auth tel userCode pkgName ip').lean({ getters: true }); + return user; + } + public static async findTokenByTel(tel: string) { const user: UserType = await UserModel.findOne({ tel }).lean(); return user ? user.token : null; diff --git a/shared/domain/sdk.ts b/shared/domain/sdk.ts new file mode 100644 index 000000000..8153f38d9 --- /dev/null +++ b/shared/domain/sdk.ts @@ -0,0 +1,42 @@ +import { prop } from "@typegoose/typegoose"; + +// 37的登录验证返回参数 +export class LoginValidateData37 { + @prop({ required: true }) + uid: number; // 用户uid + + @prop({ required: true }) + is_phone_bind: number; // 是否绑定手机 + + @prop({ required: true }) + is_idcard_bind: number; // 是否绑定身份证 + + @prop({ required: true }) + is_adult: number; // 是否成年 + + @prop({ required: true }) + vip_level: number; // vip等级 + + @prop({ required: true }) + is_youke: number; // 是否为第三方来源 + + @prop({ required: true }) + is_bind_alias: number; // 是否绑定平台个性账号 + + @prop({ required: true }) + birth: number; // 用户生日 + + @prop({ required: true }) + age: number; // 用户年龄 + + @prop({ required: true }) + unique_realname_id: string; // 用户身份证唯一标识 +} + +export interface LoginValidataReturn37 { + code: number; + msg: string; + data: LoginValidateData37; +} + +export type ChannelInfo = LoginValidataReturn37|{}; \ No newline at end of file diff --git a/shared/pubUtils/httpUtil.ts b/shared/pubUtils/httpUtil.ts index 13140ca5f..1035bde32 100644 --- a/shared/pubUtils/httpUtil.ts +++ b/shared/pubUtils/httpUtil.ts @@ -1,9 +1,65 @@ import * as request from "request-promise"; import * as crypto from 'crypto' -import { BANTU_VID_ADDR, BANTU_VID_APP_KEY } from '../consts'; +import { BANTU_VID_ADDR, BANTU_VID_APP_KEY, HTTP_METHOD } from '../consts'; +// 通用,请求http +export async function httpRequest(url: string, method: string, headers: any, body: any) { + console.log(`httpRequest*********: ${url}, ${method}, ${JSON.stringify(headers)}, ${JSON.stringify(body)}`) + + let options = { + url, + method, + headers, + body, + json: true + } + + try { + let res = await request(options); + console.log('*****request result*****'); + console.log(JSON.stringify(res)); + return res; + } catch (e) { + console.error(e); + return false + } +} + + +export function md5(str: string, treatStr?: (str: string) => string) { + if(treatStr) str = treatStr(str); + console.log('*****str', str) + return crypto.createHash('md5').update(str, 'utf8').digest("hex"); +}; + +export function getMd5ObjSign(obj: any, joinMark: string, treatStr?: (str: string) => string) { + const str = []; + Object.keys(obj).sort().forEach((key) => { + if (key == 'sign') { + + } else { + str.push(key + '=' + obj[key]); + } + }); + if (str.length == 0) { + console.error('check obj', obj); + } + var strs = str.join(joinMark); + return md5(strs, treatStr); +} + +export async function checkMd5Sign(obj: any, treatStr?: (str: string) => string) { + if (getMd5ObjSign(obj, '', treatStr) === obj.sign) { + return true; + } + console.warn('correct sign:', getMd5ObjSign(obj, '', treatStr), obj.sign); + return false; +} + + +/************** 厚土防沉迷接口 **************/ /** - * 在线报告 + * 在线报告 暂时不使用 * @param userCode 账号 * @param packageName 包名 */ @@ -24,7 +80,7 @@ export async function reportOnline(userCode: string, packageName: string) { } /** - * 实名认证 + * 实名认证 暂时不使用 * @param name 真实姓名 * @param idNum 身份证号 * @param userCode 账号 @@ -50,7 +106,7 @@ export async function authenticate(name: string, idNum: string, userCode: string } export async function vidHttpRequest(addr: string, body: any) { - body['sign'] = getObjSign(body); + body['sign'] = getVidObjSign(body); console.log('body: ', JSON.stringify(body)) let options = { @@ -62,7 +118,7 @@ export async function vidHttpRequest(addr: string, body: any) { try { let res = await request(options); - let check = checkSign(res); + let check = checkVidObjSign(res); if (!check) return false; // console.log('*****request result*****'); // console.log(JSON.stringify(res)); @@ -73,54 +129,38 @@ export async function vidHttpRequest(addr: string, body: any) { } } - -export async function httpRequest(url: string, method: string, headers: any, body: any) { - console.log(`httpRequest*********: ${url}, ${method}, ${JSON.stringify(headers)}, ${JSON.stringify(body)}`) - - let options = { - url, - method, - headers, - body, - json: true - } - - try { - let res = await request(options); - console.log('*****request result*****'); - console.log(JSON.stringify(res)); - return res; - } catch (e) { - console.error(e); - return false - } +function getVidObjSign(body: any) { + return getMd5ObjSign(body, '', treatVid); } -export function md5Vid(str: string) { - str = 'vId' + str; - return crypto.createHash('md5').update(str, 'utf8').digest("hex"); -}; - -export function getObjSign(obj: any) { - const str = []; - Object.keys(obj).sort().forEach((key) => { - if (key == 'sign') { - - } else { - str.push(key + '=' + obj[key]); - } - }); - if (str.length == 0) { - console.error('check obj', obj); - } - var strs = str.join(''); - return md5Vid(strs); +function checkVidObjSign(obj: any) { + return checkMd5Sign(obj, treatVid); } -export async function checkSign(obj: any) { - if (getObjSign(obj) === obj.sign) { - return true; - } - console.warn('correct sign:', getObjSign(obj), obj.sign); - return false; +function treatVid(str) { + return 'vId' + str; } + +// 37sdk请求 + +function get37ObjSign(body: any, key: string) { + return getMd5ObjSign(body, '&', (str) => `${str}&${key}`); +} + +export async function request37(url: string, body: any, key: string) { + // body = { + // clientid: 'abscddsssssss', + // pid: '37h5', + // game_id: 30, + // pst: 'MWNmY2pweWZ6dFRhZmdnZ1k4aHhINVUyZnFra1', + // time: 1583821808, + // c_game_id: 'com.a.test' + // } + + // key = 'FkM619)t,P7E3yK#44q85)!Sm5cv8j' + body['sign'] = get37ObjSign(body, key); + + let result = await httpRequest(url, HTTP_METHOD.POST, {}, body); + console.log('******result', result) + return result; +} \ No newline at end of file diff --git a/shared/pubUtils/sdkUtil.ts b/shared/pubUtils/sdkUtil.ts new file mode 100644 index 000000000..49b65c8f7 --- /dev/null +++ b/shared/pubUtils/sdkUtil.ts @@ -0,0 +1,34 @@ +import { SDK_37_ADDR, SDK_37_CONST } from '../consts'; +import { request37 } from './httpUtil'; +import { nowSeconds } from './timeUtil'; +import { LoginValidataReturn37 } from '../domain/sdk'; + +/************** 37sdk **************/ + +export async function loginValidate37(clientId: string, pst: string) { + let result: LoginValidataReturn37 = await request37(SDK_37_ADDR.LOGIN, { + clientid: clientId, + pid: SDK_37_CONST.PID, + game_id: SDK_37_CONST.GAME_ID, + pst, + time: nowSeconds(), + c_game_id: SDK_37_CONST.FX_C_GAME_ID + }, SDK_37_CONST.LOGIN_KEY); + if (result && result.code !== 1) { + console.error(result.msg); + } + + return result; +} + +export async function loginValidata(channelType: string, clientId: string, pst: string) { + if(channelType == '37') { + return await loginValidate37(clientId, pst); + } else { + return false; + } +} + +export function getChannelId(channelType: string, uid: number|string) { + return `${channelType}: ${uid}`; +} \ No newline at end of file diff --git a/shared/pubUtils/util.ts b/shared/pubUtils/util.ts index ead860d41..1bd61eb70 100644 --- a/shared/pubUtils/util.ts +++ b/shared/pubUtils/util.ts @@ -412,7 +412,7 @@ export function readFileAndParseJson(path: string) { let readResult = readFile(path); return JSON.parse(readResult); } catch(e) { - throw new Error(`connectors.json 格式错误:${e.message}`); + throw new Error(`connectors.json 格式错误:${(e).message}`); } } @@ -447,7 +447,7 @@ export function readFileAndParse(fileName: string) { let readResult = readJsonFile(fileName); return JSON.parse(readResult); } catch(e) { - throw new Error(`${fileName}格式错误:${e.message}`); + throw new Error(`${fileName}格式错误:${(e).message}`); } } @@ -459,7 +459,7 @@ export function readWarJsonFileAndParse() { let json = JSON.parse(str); result.push(json); } catch(e) { - throw new Error(`${name}格式错误:${e.message}`); + throw new Error(`${name}格式错误:${(e).message}`); } } return result; diff --git a/web-server/app/controller/account.ts b/web-server/app/controller/account.ts index e8df7b265..06ad6ed3d 100644 --- a/web-server/app/controller/account.ts +++ b/web-server/app/controller/account.ts @@ -61,4 +61,10 @@ export default class AccountController extends Controller { const { name, idNum } = ctx.request.body; ctx.body = await ctx.service.auth.authentication(name, idNum); } + + public async channelLogin() { + const { ctx } = this; + const { channelType, pst, clientId, deviceId, platform, pkgName, serverType, getuiCID } = ctx.request.body; + ctx.body = await ctx.service.auth.channelLogin(channelType, clientId, pst, deviceId, platform, pkgName, serverType, getuiCID); + } } diff --git a/web-server/app/controller/game.ts b/web-server/app/controller/game.ts index 838b4ef80..6c83fc58c 100644 --- a/web-server/app/controller/game.ts +++ b/web-server/app/controller/game.ts @@ -104,7 +104,7 @@ export default class GameController extends Controller { ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: true }); return; } catch (e) { - ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: false, err: e.stack }); + ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: false, err: (e).stack }); return; } } diff --git a/web-server/app/router.ts b/web-server/app/router.ts index d71032811..8160512e1 100644 --- a/web-server/app/router.ts +++ b/web-server/app/router.ts @@ -7,6 +7,7 @@ export default (app: Application) => { router.get('/dev', controller.home.dev); router.get('/dev/smscode', controller.account.getSmsCode); router.get('/', controller.home.index); + router.post('/user/channellogin', controller.account.channelLogin); router.post('/user/devicelogin', controller.account.deviceLogin); router.post('/user/getsms', controller.account.getSms); router.post('/user/smslogin', controller.account.smsLogin); diff --git a/web-server/app/service/Auth.ts b/web-server/app/service/Auth.ts index b502a9b61..ec03ac3e8 100644 --- a/web-server/app/service/Auth.ts +++ b/web-server/app/service/Auth.ts @@ -13,6 +13,8 @@ import { resResult } from '../pubUtils/util'; import { checkTeeanAgerTime } from '../pubUtils/authenticateUtil'; import { authenticate } from '../pubUtils/httpUtil'; import { checkTask } from 'app/pubUtils/taskUtil'; +import { getChannelId, loginValidata } from '../pubUtils/sdkUtil'; +import { LoginValidateData37 } from 'app/domain/sdk'; /** * Test Service @@ -381,4 +383,46 @@ export default class Auth extends Service { let date = idNum.slice(12, 14); return `${year}-${month}-${date}`; } + + /** + * 渠道服登录 + * @param channelType 渠道类型 37: '37' + * @param clientId + * @param pst + * @param deviceId + * @param platform + * @param pkgName + * @param serverType + * @param getuiCID + * @returns + */ + public async channelLogin(channelType: string, clientId: string, pst: string, deviceId: string, platform: string, pkgName: string, serverType: string, getuiCID: string) { + const ctx = this.ctx; + const ip = ctx.request.ip; + + let requestResult = await loginValidata(channelType, clientId, pst) + if(!requestResult) return this.ctx.service.utils.resResult(STATUS.CHANNEL_ERR); + if(requestResult.code != 1) { + return this.ctx.service.utils.resResult(STATUS.VALIDATE_ERR, requestResult); + } + + let channelId = getChannelId(channelType, requestResult.data.uid); + const token = ctx.service.utils.generateStr(256); + let user = await UserModel.createOrUpdateChannelUser(channelId, channelType, requestResult.data, token, platform, pkgName, serverType, deviceId, ip); + if (getuiCID) {//更新个推cid + await UserModel.updateGetuiCIDByChannel(channelId, getuiCID); + } + let channelInfo: any = {}; + if(channelType == '37') { + channelInfo.uid = (user.channelInfo).uid; + } + return ctx.service.utils.resResult(STATUS.SUCCESS, { + canLogin: true, + channelId, + token: user.token, + userCode: user.userCode, + channelInfo + }); + + } }