add user module and jwt
This commit is contained in:
parent
b8cc65a768
commit
1e043718b4
@ -31,3 +31,10 @@ export enum UserOrderType {
|
|||||||
CREATED = 'createdAt',
|
CREATED = 'createdAt',
|
||||||
UPDATED = 'updatedAt',
|
UPDATED = 'updatedAt',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const TokenConst = {
|
||||||
|
USER_TOKEN_SECRET: 'USER_TOKEN_SECRET',
|
||||||
|
DEFAULT_USER_TOKEN_SECRET: 'my-access-secret',
|
||||||
|
USER_REFRESH_TOKEN_EXPIRED: 'USER_REFRESH_TOKEN_EXPIRED',
|
||||||
|
DEFAULT_USER_REFRESH_TOKEN_EXPIRED: 'my-refresh-secret',
|
||||||
|
};
|
||||||
|
28
src/modules/user/guards/local.auth.guard.ts
Normal file
28
src/modules/user/guards/local.auth.guard.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { BadGatewayException, ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
import { plainToClass } from 'class-transformer';
|
||||||
|
|
||||||
|
import { validateOrReject } from 'class-validator';
|
||||||
|
|
||||||
|
import { CredentialDto } from '../dtos/auth.dto';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录守卫
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class LocalAuthGuard extends AuthGuard('local') {
|
||||||
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
|
try {
|
||||||
|
await validateOrReject(plainToClass(CredentialDto, request.body), {
|
||||||
|
validationError: { target: false },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const messages = (error as any[])
|
||||||
|
.map((e) => e.constraints ?? {})
|
||||||
|
.reduce((o, n) => ({ ...o, ...n }), {});
|
||||||
|
throw new BadGatewayException(Object.values(messages));
|
||||||
|
}
|
||||||
|
return super.canActivate(context) as boolean;
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,8 @@ import { AccessTokenEntity } from '@/modules/user/entities/access.token.entity';
|
|||||||
import { RefreshTokenEntity } from '@/modules/user/entities/refresh.token.entity';
|
import { RefreshTokenEntity } from '@/modules/user/entities/refresh.token.entity';
|
||||||
import { JwtConfig, JwtPayload, UserConfig } from '@/modules/user/types';
|
import { JwtConfig, JwtPayload, UserConfig } from '@/modules/user/types';
|
||||||
|
|
||||||
|
import { TokenConst } from '../constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 令牌服务
|
* 令牌服务
|
||||||
*/
|
*/
|
||||||
@ -85,7 +87,10 @@ export class TokenService {
|
|||||||
const refreshToken = new RefreshTokenEntity();
|
const refreshToken = new RefreshTokenEntity();
|
||||||
refreshToken.value = jwt.sign(
|
refreshToken.value = jwt.sign(
|
||||||
refreshTokenPayload,
|
refreshTokenPayload,
|
||||||
this.configure.env.get('USER_REFRESH_TOKEN_EXPIRED', 'my-refresh-secret'),
|
this.configure.env.get(
|
||||||
|
TokenConst.USER_REFRESH_TOKEN_EXPIRED,
|
||||||
|
TokenConst.DEFAULT_USER_REFRESH_TOKEN_EXPIRED,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
refreshToken.expiredAt = now.add(config.refreshTokenExpired, 'second').toDate();
|
refreshToken.expiredAt = now.add(config.refreshTokenExpired, 'second').toDate();
|
||||||
refreshToken.accessToken = accessToken;
|
refreshToken.accessToken = accessToken;
|
||||||
@ -136,7 +141,10 @@ export class TokenService {
|
|||||||
async verifyAccessToken(token: AccessTokenEntity) {
|
async verifyAccessToken(token: AccessTokenEntity) {
|
||||||
const result = jwt.verify(
|
const result = jwt.verify(
|
||||||
token.value,
|
token.value,
|
||||||
this.configure.env.get('USER_TOKEN_SECRET', 'my-access-secret'),
|
this.configure.env.get(
|
||||||
|
TokenConst.USER_TOKEN_SECRET,
|
||||||
|
TokenConst.DEFAULT_USER_TOKEN_SECRET,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
if (result) {
|
if (result) {
|
||||||
return token.user;
|
return token.user;
|
||||||
@ -152,7 +160,10 @@ export class TokenService {
|
|||||||
defaultUserConfig(configure),
|
defaultUserConfig(configure),
|
||||||
);
|
);
|
||||||
const options: JwtModuleOptions = {
|
const options: JwtModuleOptions = {
|
||||||
secret: configure.env.get('USER_TOKEN_SECRET', 'my-access-secret'),
|
secret: configure.env.get(
|
||||||
|
TokenConst.USER_TOKEN_SECRET,
|
||||||
|
TokenConst.DEFAULT_USER_TOKEN_SECRET,
|
||||||
|
),
|
||||||
verifyOptions: {
|
verifyOptions: {
|
||||||
ignoreExpiration: !configure.env.isProd(),
|
ignoreExpiration: !configure.env.isProd(),
|
||||||
},
|
},
|
||||||
|
37
src/modules/user/strategies/jwt.strategy.ts
Normal file
37
src/modules/user/strategies/jwt.strategy.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
|
||||||
|
import { instanceToPlain } from 'class-transformer';
|
||||||
|
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||||
|
|
||||||
|
import { Configure } from '@/modules/config/configure';
|
||||||
|
|
||||||
|
import { TokenConst } from '../constants';
|
||||||
|
import { UserRepository } from '../repositories/UserRepository';
|
||||||
|
import { JwtPayload } from '../types';
|
||||||
|
|
||||||
|
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||||
|
constructor(
|
||||||
|
protected configure: Configure,
|
||||||
|
protected userRepository: UserRepository,
|
||||||
|
) {
|
||||||
|
const secret = configure.env.get(
|
||||||
|
TokenConst.USER_TOKEN_SECRET,
|
||||||
|
TokenConst.DEFAULT_USER_TOKEN_SECRET,
|
||||||
|
);
|
||||||
|
super({
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
ignoreExpiration: false,
|
||||||
|
secretOrKey: secret,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过荷载解析出用户ID
|
||||||
|
* 通过用户ID查询出用户是否存在,并把id放入request方便后续操作
|
||||||
|
* @param payload
|
||||||
|
*/
|
||||||
|
async validate(payload: JwtPayload) {
|
||||||
|
const user = await this.userRepository.findOneOrFail({ where: { id: payload.sub } });
|
||||||
|
return instanceToPlain(user);
|
||||||
|
}
|
||||||
|
}
|
23
src/modules/user/strategies/local.strategy.ts
Normal file
23
src/modules/user/strategies/local.strategy.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
import { Strategy } from 'passport-local';
|
||||||
|
|
||||||
|
import { AuthService } from '../services/auth.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户认证本地策略
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class LocalStrategy extends PassportStrategy(Strategy) {
|
||||||
|
constructor(protected authService: AuthService) {
|
||||||
|
super({ usernameField: 'credential', passwordField: 'password' });
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(credential: string, password: string): Promise<any> {
|
||||||
|
const user = await this.authService.validateUser(credential, password);
|
||||||
|
if (!user) {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user