refactor: login session with jwt
This commit is contained in:
@@ -16,12 +16,10 @@ import {
|
||||
Query,
|
||||
} from '@nestjs/common'
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter'
|
||||
import { ApiBearerAuth, ApiOperation } from '@nestjs/swagger'
|
||||
|
||||
import { ApiController } from '~/common/decorator/api-controller.decorator'
|
||||
import { Auth } from '~/common/decorator/auth.decorator'
|
||||
import { ApiName } from '~/common/decorator/openapi.decorator'
|
||||
import { IsMaster as Master } from '~/common/decorator/role.decorator'
|
||||
import { EventBusEvents } from '~/constants/event-bus.constant'
|
||||
import { MongoIdDto } from '~/shared/dto/id.dto'
|
||||
|
||||
@@ -47,13 +45,6 @@ export class AuthController {
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: '判断当前 Token 是否有效 ' })
|
||||
@ApiBearerAuth()
|
||||
checkLogged(@Master() isMaster: boolean) {
|
||||
return { ok: ~~isMaster, isGuest: !isMaster }
|
||||
}
|
||||
|
||||
@Get('token')
|
||||
@Auth()
|
||||
async getOrVerifyToken(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Body, Get, HttpCode, Patch, Post } from '@nestjs/common'
|
||||
import { Body, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common'
|
||||
import { ApiOperation } from '@nestjs/swagger'
|
||||
|
||||
import { ApiController } from '~/common/decorator/api-controller.decorator'
|
||||
@@ -28,22 +28,35 @@ export class UserController {
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: '获取主人信息' })
|
||||
async getMasterInfo(@IsMaster() isMaster: boolean) {
|
||||
return await this.userService.getMasterInfo(isMaster)
|
||||
}
|
||||
|
||||
@Post('register')
|
||||
@ApiOperation({ summary: '注册' })
|
||||
@Post('/register')
|
||||
async register(@Body() userDto: UserDto) {
|
||||
userDto.name = userDto.name ?? userDto.username
|
||||
return await this.userService.createMaster(userDto as UserModel)
|
||||
}
|
||||
|
||||
@Post('login')
|
||||
@ApiOperation({ summary: '登录' })
|
||||
@Put('/login')
|
||||
@Auth()
|
||||
async loginWithToken(
|
||||
@IpLocation() ipLocation: IpRecord,
|
||||
@CurrentUser() user: UserDocument,
|
||||
@CurrentUserToken() token: string,
|
||||
) {
|
||||
await this.authService.jwtServicePublic.revokeToken(token)
|
||||
await this.userService.recordFootstep(ipLocation.ip)
|
||||
return {
|
||||
token: this.authService.jwtServicePublic.sign(user._id, {
|
||||
ip: ipLocation.ip,
|
||||
ua: ipLocation.agent,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@Post('/login')
|
||||
@HttpCache({ disable: true })
|
||||
@HttpCode(200)
|
||||
async login(@Body() dto: LoginDto, @IpLocation() ipLocation: IpRecord) {
|
||||
const user = await this.userService.login(dto.username, dto.password)
|
||||
const footstep = await this.userService.recordFootstep(ipLocation.ip)
|
||||
@@ -51,7 +64,10 @@ export class UserController {
|
||||
const avatar = user.avatar ?? getAvatar(mail)
|
||||
|
||||
return {
|
||||
token: this.authService.jwtServicePublic.sign(user._id),
|
||||
token: this.authService.jwtServicePublic.sign(user._id, {
|
||||
ip: ipLocation.ip,
|
||||
ua: ipLocation.agent,
|
||||
}),
|
||||
...footstep,
|
||||
name,
|
||||
username,
|
||||
@@ -65,14 +81,12 @@ export class UserController {
|
||||
|
||||
@Get('check_logged')
|
||||
@ApiOperation({ summary: '判断当前 Token 是否有效 ' })
|
||||
@Auth()
|
||||
@HttpCache.disable
|
||||
checkLogged(@IsMaster() isMaster: boolean) {
|
||||
return { ok: +isMaster, isGuest: !isMaster }
|
||||
}
|
||||
|
||||
@Patch()
|
||||
@ApiOperation({ summary: '修改主人的信息' })
|
||||
@Auth()
|
||||
@HttpCache.disable
|
||||
@BanInDemo
|
||||
@@ -83,9 +97,27 @@ export class UserController {
|
||||
return await this.userService.patchUserData(user, body)
|
||||
}
|
||||
|
||||
@Post('logout')
|
||||
@Post('/logout')
|
||||
@Auth()
|
||||
async singout(@CurrentUserToken() token: string) {
|
||||
return this.userService.signout(token)
|
||||
}
|
||||
|
||||
@Get('/session')
|
||||
@Auth()
|
||||
async getAllSession(@CurrentUserToken() token: string) {
|
||||
return this.authService.jwtServicePublic.getAllSignSession(token)
|
||||
}
|
||||
|
||||
@Delete('/session/:tokenId')
|
||||
@Auth()
|
||||
async deleteSession(@Param('tokenId') tokenId: string) {
|
||||
return this.authService.jwtServicePublic.revokeToken(tokenId)
|
||||
}
|
||||
|
||||
@Delete('/session/all')
|
||||
@Auth()
|
||||
async deleteAllSession() {
|
||||
return this.authService.jwtServicePublic.revokeAll()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,10 @@ import {
|
||||
import { ReturnModelType } from '@typegoose/typegoose'
|
||||
|
||||
import { BusinessException } from '~/common/exceptions/business.exception'
|
||||
import { RedisKeys } from '~/constants/cache.constant'
|
||||
import { ErrorCodeEnum } from '~/constants/error-code.constant'
|
||||
import { CacheService } from '~/processors/redis/cache.service'
|
||||
import { InjectModel } from '~/transformers/model.transformer'
|
||||
import { getAvatar, sleep } from '~/utils'
|
||||
import { getRedisKey } from '~/utils/redis.util'
|
||||
|
||||
import { AuthService } from '../auth/auth.service'
|
||||
import { UserDocument, UserModel } from './user.model'
|
||||
@@ -80,7 +78,6 @@ export class UserService {
|
||||
throw new BadRequestException('我已经有一个主人了哦')
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const res = await this.userModel.create({ ...model })
|
||||
const token = this.authService.jwtServicePublic.sign(res._id)
|
||||
return { token, username: res.username }
|
||||
@@ -142,19 +139,8 @@ export class UserService {
|
||||
lastLoginTime: new Date(),
|
||||
lastLoginIp: ip,
|
||||
})
|
||||
// save to redis
|
||||
process.nextTick(async () => {
|
||||
const redisClient = this.redis.getClient()
|
||||
|
||||
await redisClient.sadd(
|
||||
getRedisKey(RedisKeys.LoginRecord),
|
||||
JSON.stringify({ date: new Date().toISOString(), ip }),
|
||||
)
|
||||
})
|
||||
|
||||
this.Logger.warn(`主人已登录, IP: ${ip}`)
|
||||
return PrevFootstep as any
|
||||
}
|
||||
|
||||
// TODO 获取最近登陆次数 时间 从 Redis 取
|
||||
}
|
||||
|
||||
@@ -65,10 +65,26 @@ export class JWTService {
|
||||
return !!has
|
||||
}
|
||||
|
||||
async getAllSignSession(currentToken?: string) {
|
||||
const redis = this.cacheService.getClient()
|
||||
const res = await redis.hgetall(getRedisKey(RedisKeys.JWTStore))
|
||||
const hashedCurrent = currentToken && md5(currentToken)
|
||||
return Object.entries(res).map(([k, v]) => {
|
||||
return {
|
||||
...JSON.parse(v),
|
||||
id: `jwt-${k}`,
|
||||
current: hashedCurrent === k,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async revokeToken(token: string) {
|
||||
const redis = this.cacheService.getClient()
|
||||
const key = getRedisKey(RedisKeys.JWTStore)
|
||||
await redis.hdel(key, md5(token))
|
||||
await redis.hdel(
|
||||
key,
|
||||
token.startsWith(`jwt-`) ? token.replace(`jwt-`, '') : md5(token),
|
||||
)
|
||||
}
|
||||
|
||||
async revokeAll() {
|
||||
@@ -77,22 +93,23 @@ export class JWTService {
|
||||
await redis.del(key)
|
||||
}
|
||||
|
||||
async storeTokenInRedis(token: string) {
|
||||
async storeTokenInRedis(token: string, info?: any) {
|
||||
const redis = this.cacheService.getClient()
|
||||
await redis.hset(
|
||||
getRedisKey(RedisKeys.JWTStore),
|
||||
md5(token),
|
||||
JSON.stringify({
|
||||
date: new Date().toISOString(),
|
||||
...info,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
sign(id: string) {
|
||||
sign(id: string, info?: { ip: string; ua: string }) {
|
||||
const token = sign({ id }, this.secret, {
|
||||
expiresIn: '1y',
|
||||
expiresIn: '30d',
|
||||
})
|
||||
this.storeTokenInRedis(token)
|
||||
this.storeTokenInRedis(token, info || {})
|
||||
return token
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user