add user module
This commit is contained in:
parent
040b0b71d5
commit
46253b3a4f
@ -12,8 +12,8 @@ export function defaultUserConfig(configure: Configure): UserConfig {
|
|||||||
return {
|
return {
|
||||||
hash: 10,
|
hash: 10,
|
||||||
jwt: {
|
jwt: {
|
||||||
token_expired: configure.env.get('USER_TOKEN_EXPIRED', (v) => toNumber(v), 1800),
|
tokenExpired: configure.env.get('USER_TOKEN_EXPIRED', (v) => toNumber(v), 1800),
|
||||||
refresh_token_expired: configure.env.get(
|
refreshTokenExpired: configure.env.get(
|
||||||
'USER_REFRESH_TOKEN_EXPIRED',
|
'USER_REFRESH_TOKEN_EXPIRED',
|
||||||
(v) => toNumber(v),
|
(v) => toNumber(v),
|
||||||
3600 * 30,
|
3600 * 30,
|
||||||
|
@ -13,7 +13,7 @@ export class AccessTokenEntity extends BaseToken {
|
|||||||
* 关联的刷新令牌
|
* 关联的刷新令牌
|
||||||
*/
|
*/
|
||||||
@OneToOne(() => RefreshTokenEntity, (token) => token.accessToken, { cascade: true })
|
@OneToOne(() => RefreshTokenEntity, (token) => token.accessToken, { cascade: true })
|
||||||
refreshToken: string;
|
refreshToken: RefreshTokenEntity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关联用户
|
* 关联用户
|
||||||
|
@ -15,7 +15,7 @@ export abstract class BaseToken extends BaseEntity {
|
|||||||
@Column({
|
@Column({
|
||||||
comment: '令牌过期时间',
|
comment: '令牌过期时间',
|
||||||
})
|
})
|
||||||
expired_at: Date;
|
expiredAt: Date;
|
||||||
|
|
||||||
@CreateDateColumn({
|
@CreateDateColumn({
|
||||||
comment: '令牌创建时间',
|
comment: '令牌创建时间',
|
||||||
|
142
src/modules/user/services/token.service.ts
Normal file
142
src/modules/user/services/token.service.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
import { Configure } from '@/modules/config/configure';
|
||||||
|
import { getTime } from '@/modules/core/helpers/time';
|
||||||
|
import { getUserConfig } from '@/modules/user/config';
|
||||||
|
import { UserEntity } from '@/modules/user/entities/UserEntity';
|
||||||
|
import { AccessTokenEntity } from '@/modules/user/entities/access.token.entity';
|
||||||
|
import { RefreshTokenEntity } from '@/modules/user/entities/refresh.token.entity';
|
||||||
|
import { JwtConfig, JwtPayload } from '@/modules/user/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 令牌服务
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class TokenService {
|
||||||
|
constructor(
|
||||||
|
protected configure: Configure,
|
||||||
|
protected jwtService: JwtService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据accessToken刷新AccessToken与RefreshToken
|
||||||
|
* @param accessToken
|
||||||
|
* @param response
|
||||||
|
*/
|
||||||
|
async refreshToken(accessToken: AccessTokenEntity, response: Response) {
|
||||||
|
const { user, refreshToken } = accessToken;
|
||||||
|
if (refreshToken) {
|
||||||
|
const now = await getTime(this.configure);
|
||||||
|
if (now.isAfter(refreshToken.expiredAt)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const token = await this.generateAccessToken(user, now);
|
||||||
|
await accessToken.remove();
|
||||||
|
response.header('token', token.accessToken.value);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据荷载签出新的AccessToken并存入数据库
|
||||||
|
* 且自动生成新的Refresh也存入数据库
|
||||||
|
* @param user
|
||||||
|
* @param now
|
||||||
|
*/
|
||||||
|
async generateAccessToken(
|
||||||
|
user: UserEntity,
|
||||||
|
now: dayjs.Dayjs,
|
||||||
|
): Promise<{ accessToken: AccessTokenEntity; refreshToken: RefreshTokenEntity }> {
|
||||||
|
const config = await getUserConfig<JwtConfig>(this.configure, 'jwt');
|
||||||
|
const accessTokenPayload: JwtPayload = { sub: user.id, iat: now.unix() };
|
||||||
|
const signed = this.jwtService.sign(accessTokenPayload);
|
||||||
|
const accessToken = new AccessTokenEntity();
|
||||||
|
accessToken.value = signed;
|
||||||
|
accessToken.user = user;
|
||||||
|
accessToken.expiredAt = now.add(config.tokenExpired, 'second').toDate();
|
||||||
|
await accessToken.save();
|
||||||
|
const refreshToken = await this.generateRefreshToken(
|
||||||
|
accessToken,
|
||||||
|
await getTime(this.configure),
|
||||||
|
);
|
||||||
|
return { accessToken, refreshToken };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成新的RefreshToken并存入数据库
|
||||||
|
* @param accessToken
|
||||||
|
* @param now
|
||||||
|
*/
|
||||||
|
async generateRefreshToken(
|
||||||
|
accessToken: AccessTokenEntity,
|
||||||
|
now: dayjs.Dayjs,
|
||||||
|
): Promise<RefreshTokenEntity> {
|
||||||
|
const config = await getUserConfig<JwtConfig>(this.configure, 'jwt');
|
||||||
|
const refreshTokenPayload = { uuid: uuid() };
|
||||||
|
const refreshToken = new RefreshTokenEntity();
|
||||||
|
refreshToken.value = this.jwtService.sign(
|
||||||
|
refreshTokenPayload,
|
||||||
|
this.configure.env.get('USER_REFRESH_TOKEN_EXPIRED', 'my-refresh-secret'),
|
||||||
|
);
|
||||||
|
refreshToken.expiredAt = now.add(config.refreshTokenExpired, 'second').toDate();
|
||||||
|
refreshToken.accessToken = accessToken;
|
||||||
|
await refreshToken.save();
|
||||||
|
return refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查accessToken是否存在
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
async checkAccessToken(value: string) {
|
||||||
|
return AccessTokenEntity.findOne({ where: { value }, relations: ['user', 'refreshToken'] });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除AccessToken且自动移除关联的RefreshToken
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
async removeAccessToken(value: string) {
|
||||||
|
const accessToken = await AccessTokenEntity.findOne({ where: { value } });
|
||||||
|
if (accessToken) {
|
||||||
|
await accessToken.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除RefreshToken
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
async removeRefreshToken(value: string) {
|
||||||
|
const refreshToken = await RefreshTokenEntity.findOne({
|
||||||
|
where: { value },
|
||||||
|
relations: ['accessToken'],
|
||||||
|
});
|
||||||
|
if (refreshToken) {
|
||||||
|
if (refreshToken.accessToken) {
|
||||||
|
await refreshToken.accessToken.remove();
|
||||||
|
}
|
||||||
|
await refreshToken.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证Token是否正确,如果正确则返回所属用户对象
|
||||||
|
* @param token
|
||||||
|
*/
|
||||||
|
async verifyAccessToken(token: AccessTokenEntity) {
|
||||||
|
const result = this.jwtService.verify(
|
||||||
|
token.value,
|
||||||
|
this.configure.env.get('USER_TOKEN_SECRET', 'my-access-secret'),
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
return token.user;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -19,11 +19,11 @@ export interface JwtConfig {
|
|||||||
/**
|
/**
|
||||||
* token过期时间
|
* token过期时间
|
||||||
*/
|
*/
|
||||||
token_expired: number;
|
tokenExpired: number;
|
||||||
/**
|
/**
|
||||||
* refresh token
|
* refresh token
|
||||||
*/
|
*/
|
||||||
refresh_token_expired: number;
|
refreshTokenExpired: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user