按官方说明,鉴权主要分两步:
① LocalStrategy本地策略对用户名和密码进行比对
② 第①项登录成功后,JwtStrategy根据payload签名返回token;或者从Headers解析验证token是否有效
部署流程
① 需要创建auth module, auth service, strategy(local/jwt), guard(local/ jwt)
② 总体原理就是:
// auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { LocalStrategy } from './strategy/local';
import { JwtStrategy } from './strategy/jwt';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Users } from 'src/userinfo/entities/users.entity';
@Module({
imports: [
// UserinfoModule,
TypeOrmModule.forFeature([Users]),
PassportModule,
JwtModule.register({
secret: 'TEMPsecret',
signOptions: { expiresIn: '120s' }
})
], //
providers: [AuthService , LocalStrategy, JwtStrategy],
exports: [ AuthService ]
})
export class AuthModule {}
// auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { InjectRepository } from '@nestjs/typeorm';
import { Users } from 'src/userinfo/entities/users.entity';
import { Repository } from 'typeorm';
@Injectable()
export class AuthService {
constructor(
@InjectRepository(Users) private readonly usersRepository: // 调用数据库必须进行注入
Repository,
private jwtService: JwtService
){}
async validateUser(username: string, password: string ): Promise {
const user = await this.usersRepository.findOne({ where: {username} })
if (user && user.password === password) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(userinfo) {
const payload = { username: userinfo.username, sub: 'any msg' };
return {
access_token: this.jwtService.sign(payload),
};
}
}
// jwt.strategy.ts
import { Strategy } from 'passport-jwt'; //注意此处引入来源和local不同
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { ExtractJwt } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'TEMPsecret',
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
//local.strategy.ts
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from '../auth.service';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}
// 此函数会在useguard装饰后直接执行进行校验
// 如果传递的是json数据会有异常,所以暂时还是改用表单数据
async validate(username: string, password: string): Promise {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
// 这里返回用户信息供后面进行payload传token
return user;
}
}
// auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
// 定义两个passport的守卫类型
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
需要用到的controller文件里引用UseGuards
@UseGuards(LocalAuthGuard)
@Post('login')
signIn(@Body() userinfo: any){
// console.log("🚀 ~ file: userinfo.controller.ts:30 ~ UserinfoController ~ signIn ~ userinfo:", userinfo)
// 如果上面守卫校验通过了,则会执行下面的登录返回token时间
return this.authService.login(userinfo)
}
// 其他需要验证token的请求直接添加装饰器即可
// @UseGuards(JwtAuthGuard)
记得到本模块module里引入AuthModule, 不然无法调用authService
以上代码有个缺点,所有接口都定义在引入module里,这样会导致调用数据库时service循环引用(曲线解决: authModule里直接引用数据库查询TypeOrmModule),所以最好是将注册认证接口定义到authModule的controller控制器里,然后调用userService即可