feat: add signout
This commit is contained in:
@@ -55,7 +55,6 @@ export class UserController {
|
||||
url,
|
||||
mail,
|
||||
avatar,
|
||||
expiresIn: 7,
|
||||
id,
|
||||
}
|
||||
}
|
||||
@@ -79,4 +78,9 @@ export class UserController {
|
||||
) {
|
||||
return await this.userService.patchUserData(user, body)
|
||||
}
|
||||
|
||||
@Post('signout')
|
||||
async singout(@CurrentUser() user: any) {
|
||||
return this.userService.signout(user.token)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ export class UserService {
|
||||
|
||||
// @ts-ignore
|
||||
const res = await this.userModel.create({ ...model })
|
||||
const token = await this.authService.jwtServicePublic.sign(res._id)
|
||||
const token = this.authService.jwtServicePublic.sign(res._id)
|
||||
return { token, username: res.username }
|
||||
}
|
||||
|
||||
@@ -112,11 +112,15 @@ export class UserService {
|
||||
}
|
||||
|
||||
// 2. 撤销所有 token
|
||||
await this.authService.jwtServicePublic.invokeAll()
|
||||
await this.authService.jwtServicePublic.revokeAll()
|
||||
}
|
||||
return await this.userModel.updateOne({ _id: user._id }, doc)
|
||||
}
|
||||
|
||||
signout(token: string) {
|
||||
return this.authService.jwtServicePublic.revokeToken(token)
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登陆的足迹(ip, 时间)
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import cluster from 'cluster'
|
||||
import { SignOptions, sign, verify } from 'jsonwebtoken'
|
||||
import { sign, verify } from 'jsonwebtoken'
|
||||
import { machineIdSync } from 'node-machine-id'
|
||||
|
||||
import { Injectable } from '@nestjs/common'
|
||||
@@ -10,37 +10,47 @@ import { getRedisKey, md5 } from '~/utils'
|
||||
|
||||
import { CacheService } from '../cache/cache.service'
|
||||
|
||||
const getMachineId = () => {
|
||||
const id = machineIdSync()
|
||||
|
||||
if (isDev && cluster.isPrimary) {
|
||||
console.log(id)
|
||||
}
|
||||
return id
|
||||
}
|
||||
const secret =
|
||||
SECURITY.jwtSecret ||
|
||||
Buffer.from(getMachineId()).toString('base64').slice(0, 15) ||
|
||||
'asjhczxiucipoiopiqm2376'
|
||||
|
||||
if (isDev && cluster.isPrimary) {
|
||||
console.log(secret)
|
||||
}
|
||||
if (!CLUSTER.enable || cluster.isPrimary) {
|
||||
console.log(
|
||||
'JWT Secret start with :',
|
||||
secret.slice(0, 5) + '*'.repeat(secret.length - 5),
|
||||
)
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class JWTService {
|
||||
constructor(private readonly cacheService: CacheService) {}
|
||||
private secret = ''
|
||||
constructor(private readonly cacheService: CacheService) {
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this.secret) {
|
||||
return
|
||||
}
|
||||
|
||||
const getMachineId = () => {
|
||||
const id = machineIdSync()
|
||||
|
||||
if (isDev && cluster.isPrimary) {
|
||||
console.log(id)
|
||||
}
|
||||
return id
|
||||
}
|
||||
const secret =
|
||||
SECURITY.jwtSecret ||
|
||||
Buffer.from(getMachineId()).toString('base64').slice(0, 15) ||
|
||||
'asjhczxiucipoiopiqm2376'
|
||||
|
||||
if (isDev && cluster.isPrimary) {
|
||||
console.log(secret)
|
||||
}
|
||||
if (!CLUSTER.enable || cluster.isPrimary) {
|
||||
console.log(
|
||||
'JWT Secret start with :',
|
||||
secret.slice(0, 5) + '*'.repeat(secret.length - 5),
|
||||
)
|
||||
}
|
||||
this.secret = secret
|
||||
}
|
||||
|
||||
async verify(token: string) {
|
||||
try {
|
||||
verify(token, secret)
|
||||
return await this.isTokenInRedis(token)
|
||||
verify(token, this.secret)
|
||||
return isDev ? true : await this.isTokenInRedis(token)
|
||||
} catch (er) {
|
||||
console.debug(er, token)
|
||||
|
||||
@@ -51,17 +61,17 @@ export class JWTService {
|
||||
async isTokenInRedis(token: string) {
|
||||
const redis = this.cacheService.getClient()
|
||||
const key = getRedisKey(RedisKeys.JWTStore)
|
||||
const has = await redis.sismember(key, md5(token))
|
||||
const has = await redis.hexists(key, md5(token))
|
||||
return !!has
|
||||
}
|
||||
|
||||
async invokeToken(token: string) {
|
||||
async revokeToken(token: string) {
|
||||
const redis = this.cacheService.getClient()
|
||||
const key = getRedisKey(RedisKeys.JWTStore)
|
||||
await redis.srem(key, md5(token))
|
||||
await redis.hdel(key, md5(token))
|
||||
}
|
||||
|
||||
async invokeAll() {
|
||||
async revokeAll() {
|
||||
const redis = this.cacheService.getClient()
|
||||
const key = getRedisKey(RedisKeys.JWTStore)
|
||||
await redis.del(key)
|
||||
@@ -69,11 +79,17 @@ export class JWTService {
|
||||
|
||||
async storeTokenInRedis(token: string) {
|
||||
const redis = this.cacheService.getClient()
|
||||
await redis.sadd(getRedisKey(RedisKeys.JWTStore), md5(token))
|
||||
await redis.hset(
|
||||
getRedisKey(RedisKeys.JWTStore),
|
||||
md5(token),
|
||||
JSON.stringify({
|
||||
date: new Date().toISOString(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
sign(id: string, options: SignOptions = { expiresIn: '7d' }) {
|
||||
const token = sign({ id }, secret, options)
|
||||
sign(id: string) {
|
||||
const token = sign({ id }, this.secret)
|
||||
this.storeTokenInRedis(token)
|
||||
return token
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user