增加token刷新机制
This commit is contained in:
parent
5982af8ce6
commit
e82053f7d8
@ -4,6 +4,9 @@ API_TOKEN_SECRET_PRIVATE=MC4CAQAwBQYDK2VwBCIEIKdK/eFQ2+Q/ml4ruDAItNIwGnQMQm76UX0
|
||||
API_TOKEN_SECRET_PUBLIC=MCowBQYDK2VwAyEAySgE/YiiI2fzpXaco+OWeDAKymEoqqLYYb6RKOEU1n8=
|
||||
API_TOKEN_EXPIRESIN=1d
|
||||
|
||||
REFRESH_TOKEN_SECRET_PRIVATE=MC4CAQAwBQYDK2VwBCIEIMNKHEo6d3B6O4SiB4a5cFgKNNCMGj0BaRhPx5wG3DrZ
|
||||
REFRESH_TOKEN_SECRET_PUBLIC=MCowBQYDK2VwAyEAWFiOqbdxFu1XW5MoI3YeVRBZ4JoEWQMwXg49v1ssaXM=
|
||||
|
||||
|
||||
GOOGLE_OAUTH_CLIENT="53206975661-asnf3qe4bg29p8h981pgf099osvrjbme.apps.googleusercontent.com"
|
||||
GOOGLE_OAUTH_CLIENT2="53206975661-ih3r0ubph3rqejdq97b029difbrk2bqj.apps.googleusercontent.com"
|
||||
|
@ -4,6 +4,9 @@ API_TOKEN_SECRET_PRIVATE=MC4CAQAwBQYDK2VwBCIEIKdK/eFQ2+Q/ml4ruDAItNIwGnQMQm76UX0
|
||||
API_TOKEN_SECRET_PUBLIC=MCowBQYDK2VwAyEAySgE/YiiI2fzpXaco+OWeDAKymEoqqLYYb6RKOEU1n8=
|
||||
API_TOKEN_EXPIRESIN=1d
|
||||
|
||||
REFRESH_TOKEN_SECRET_PRIVATE=MC4CAQAwBQYDK2VwBCIEIMNKHEo6d3B6O4SiB4a5cFgKNNCMGj0BaRhPx5wG3DrZ
|
||||
REFRESH_TOKEN_SECRET_PUBLIC=MCowBQYDK2VwAyEAWFiOqbdxFu1XW5MoI3YeVRBZ4JoEWQMwXg49v1ssaXM=
|
||||
|
||||
|
||||
GOOGLE_OAUTH_CLIENT="53206975661-asnf3qe4bg29p8h981pgf099osvrjbme.apps.googleusercontent.com"
|
||||
GOOGLE_OAUTH_CLIENT2="53206975661-ih3r0ubph3rqejdq97b029difbrk2bqj.apps.googleusercontent.com"
|
||||
|
@ -13,7 +13,9 @@ import { PlatFacebook } from 'plats/PlatFacebook'
|
||||
import { PlatGoogle } from 'plats/PlatGoogle'
|
||||
import { PlatTikTok } from 'plats/PlatTikTok'
|
||||
import { checkReleation } from 'service/game.svr'
|
||||
import { generateRefreshToken, verifyRefreshToken } from 'utils/jwt.utils'
|
||||
import { ZError, BaseController, role, ROLE_ANON, router } from 'zutils'
|
||||
import { uuid } from 'zutils/utils/security.util'
|
||||
|
||||
const plats: Map<PlatEnum, IPlat> = new Map([
|
||||
[PlatEnum.GOOGLE, new PlatGoogle()],
|
||||
@ -98,7 +100,8 @@ class LoginController extends BaseController {
|
||||
@role(ROLE_ANON)
|
||||
@router('post /wallet/login/general')
|
||||
async generalLogin(req, res) {
|
||||
const { code, channel, account } = req.params
|
||||
// nb: 是否不返回unionAccount相关信息
|
||||
const { code, channel, account, nb } = req.params
|
||||
logger.db('login', req)
|
||||
if (!code) {
|
||||
throw new ZError(10, 'code not found')
|
||||
@ -113,7 +116,13 @@ class LoginController extends BaseController {
|
||||
data.platform = api_platform
|
||||
}
|
||||
const user = await Account.insertOrUpdate({ plat: channel, openId }, data)
|
||||
const { unionAccount, walletUser } = await parseBindAccount(account, channel, user)
|
||||
let unionAccount = {id: '', gameAccount: ''}
|
||||
let walletUser = user
|
||||
if (!nb) {
|
||||
const res = await parseBindAccount(account, channel, user)
|
||||
unionAccount = res.unionAccount
|
||||
walletUser = res.walletUser
|
||||
}
|
||||
if (plat.afterLogin) {
|
||||
await plat.afterLogin(user)
|
||||
}
|
||||
@ -125,6 +134,46 @@ class LoginController extends BaseController {
|
||||
version: walletUser.accountVersion || 0,
|
||||
plat: walletUser.plat,
|
||||
})
|
||||
return { token: ztoken }
|
||||
const refreshTokenKey = uuid()
|
||||
walletUser.refreshTime = Date.now()
|
||||
walletUser.refreshTokenKey = refreshTokenKey
|
||||
await walletUser.save()
|
||||
const refreshToken1 = generateRefreshToken({ id: refreshTokenKey })
|
||||
return { token: ztoken, refreshToken: refreshToken1 }
|
||||
}
|
||||
|
||||
@role(ROLE_ANON)
|
||||
@router('post /wallet/refresh_token')
|
||||
async refreshToken(req, res) {
|
||||
logger.db('refresh_token', req)
|
||||
const { refreshToken } = req.params
|
||||
if (!refreshToken) {
|
||||
throw new ZError(10, 'no refresh token')
|
||||
}
|
||||
const tokenData = verifyRefreshToken(refreshToken)
|
||||
if (!tokenData || !tokenData.id) {
|
||||
throw new ZError(11, 'refresh token invalid')
|
||||
}
|
||||
const user = await Account.findByRefreshToken(tokenData.id)
|
||||
if (!user) {
|
||||
throw new ZError(12, 'account not found')
|
||||
}
|
||||
if (user.locked) {
|
||||
throw new ZError(13, 'account locked')
|
||||
}
|
||||
const refreshTokenKey = uuid()
|
||||
user.refreshTime = Date.now()
|
||||
user.refreshTokenKey = refreshTokenKey
|
||||
await user.save()
|
||||
const refreshToken1 = generateRefreshToken({ id: refreshTokenKey })
|
||||
const ztoken = await res.jwtSign({
|
||||
id: user.id,
|
||||
uid: '',
|
||||
gid: '',
|
||||
openid: user.openId,
|
||||
version: user.accountVersion || 0,
|
||||
plat: user.plat,
|
||||
})
|
||||
return { refreshToken: refreshToken1, token: ztoken }
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { getModelForClass, index, modelOptions, mongoose, prop, ReturnModelType,
|
||||
import { dbconn } from 'decorators/dbconn'
|
||||
import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses'
|
||||
import { BaseModule } from './Base'
|
||||
import { genRandomString, sha512 } from 'zutils/utils/security.util'
|
||||
import { genRandomString, sha512, uuid } from 'zutils/utils/security.util'
|
||||
import { PlatEnum } from 'enums/PlatEnum'
|
||||
|
||||
/**
|
||||
@ -30,6 +30,7 @@ export function verifyPass(userpassword: string, passwordDb: string, salt: strin
|
||||
export interface AccountClass extends Base, TimeStamps {}
|
||||
@dbconn()
|
||||
@index({ plat: 1, openId: 1 }, { unique: true })
|
||||
@index({ refreshTokenKey: 1 }, { unique: false })
|
||||
@index({ plat: 1, email: 1 }, { collation: { locale: 'en', strength: 2 } })
|
||||
@modelOptions({ schemaOptions: { collection: 'account', timestamps: true }, options: { allowMixed: Severity.ALLOW } })
|
||||
export class AccountClass extends BaseModule {
|
||||
@ -100,7 +101,12 @@ export class AccountClass extends BaseModule {
|
||||
@prop()
|
||||
public platform: string
|
||||
|
||||
public static async findByEmail(this: ReturnModelType<typeof AccountClass>, email) {
|
||||
@prop()
|
||||
public refreshTokenKey: string
|
||||
@prop()
|
||||
public refreshTime: number
|
||||
|
||||
public static async findByEmail(this: ReturnModelType<typeof AccountClass>, email: string) {
|
||||
return this.findOne({ email, plat: PlatEnum.EMAIL }).exec()
|
||||
}
|
||||
|
||||
@ -112,6 +118,11 @@ export class AccountClass extends BaseModule {
|
||||
}
|
||||
}
|
||||
|
||||
public static async findByRefreshToken(this: ReturnModelType<typeof AccountClass>, refreshToken
|
||||
: string) {
|
||||
return this.findOne({ refreshTokenKey: refreshToken, deleted: false }).exec()
|
||||
}
|
||||
|
||||
public verifyPassword(password: string) {
|
||||
return verifyPass(password, this.password, this.salt)
|
||||
}
|
||||
|
27
src/utils/jwt.utils.ts
Normal file
27
src/utils/jwt.utils.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { createSigner, createVerifier } from 'fast-jwt'
|
||||
|
||||
const privateKey = `-----BEGIN PRIVATE KEY-----
|
||||
${process.env.REFRESH_TOKEN_SECRET_PRIVATE}
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
const publicKey = `-----BEGIN PUBLIC KEY-----
|
||||
${process.env.REFRESH_TOKEN_SECRET_PUBLIC}
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
const REFRESH_TOKEN_EXPIRES_IN = 30 * 24 * 60 * 60 * 1000
|
||||
export const generateRefreshToken = (data: any) => {
|
||||
const signSync = createSigner({
|
||||
algorithm: 'EdDSA',
|
||||
expiresIn: REFRESH_TOKEN_EXPIRES_IN,
|
||||
key: privateKey,
|
||||
})
|
||||
return signSync(data)
|
||||
}
|
||||
|
||||
export const verifyRefreshToken = (token: string) => {
|
||||
const verifier = createVerifier({
|
||||
algorithms: ['EdDSA'],
|
||||
key: publicKey,
|
||||
})
|
||||
return verifier(token)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user