前言
官网
流程
nest 新开个项目 nest new xxx 按照指示,安装:
$ npm install --save @nestjs/passport passport passport-local
$ npm install --save-dev @types/passport-local
$ nest g module auth
$ nest g service auth
$ nest g module users
$ nest g service users
import { Injectable } from '@nestjs/common' ;
export type User = any ;
@Injectable ( )
export class UsersService {
private readonly users: User[ ] ;
constructor ( ) {
this . users = [
{
userId: 1 ,
username: 'john' ,
password: 'changeme' ,
} ,
{
userId: 2 ,
username: 'chris' ,
password: 'secret' ,
} ,
{
userId: 3 ,
username: 'maria' ,
password: 'guess' ,
} ,
] ;
}
async findOne ( username: string ) : Promise< User | undefined> {
return this . users. find ( user => user. username === username) ;
}
}
exports: [UsersService],
import { Injectable } from '@nestjs/common' ;
import { UsersService } from '../users/users.service' ;
@Injectable ( )
export class AuthService {
constructor ( private usersService: UsersService) { }
async validateUser ( username: string , pass: string ) : Promise< any > {
const user = await this . usersService. findOne ( username) ;
if ( user && user. password === pass) {
const { password, ... result } = user;
return result;
}
return null ;
}
}
同时auth的模块中导入user的模块 新建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 ( ) ;
}
async validate ( username: string , password: string ) : Promise< any > {
const user = await this . authService. validateUser ( username, password) ;
if ( ! user) {
throw new UnauthorizedException ( ) ;
}
return user;
}
}
使用别的字段在super里改super({ usernameField: ‘email’ }) 然后在模块中导入passportModule,提供上面建立的服务:
@Module ( {
imports: [ UsersModule, PassportModule] ,
providers: [ AuthService, LocalStrategy] ,
} )
在appcontroller里面用守卫做个Post路由:
import { Controller, Request, Post, UseGuards } from '@nestjs/common' ;
import { AuthGuard } from '@nestjs/passport' ;
@Controller ( )
export class AppController {
@UseGuards ( AuthGuard ( 'local' ) )
@Post ( 'auth/login' )
async login ( @Request ( ) req) {
return req. user;
}
}
{
"username": "john",
"password": "changeme"
}
则可以正确返回user ,否则直接跳401。 还可以进行转换下,新建local-auth.guard.ts:
import { Injectable } from '@nestjs/common' ;
import { AuthGuard } from '@nestjs/passport' ;
@Injectable ( )
export class LocalAuthGuard extends AuthGuard ( 'local' ) { }
@UseGuards(LocalAuthGuard)
结合jwt
$ npm install --save @nestjs/jwt passport-jwt
$ npm install --save-dev @types/passport-jwt
首先在auth服务里声明jwtService并增加登录方法:
import { Injectable } from '@nestjs/common' ;
import { JwtService } from '@nestjs/jwt' ;
import { UsersService } from '../users/users.service' ;
@Injectable ( )
export class AuthService {
constructor (
private usersService: UsersService,
private jwtService: JwtService,
) { }
async validateUser ( username: string , pass: string ) : Promise< any > {
const user = await this . usersService. findOne ( username) ;
if ( user && user. password === pass) {
const { password, ... result } = user;
return result;
}
return null ;
}
async login ( user: any ) {
const payload = { username: user. username, sub: user. userId } ;
return {
access_token: this . jwtService. sign ( payload) ,
} ;
}
}
export const jwtConstants = {
secret: 'secretKey',
};
JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: '60s' },
}),
jwt更多配置 修改控制器路由,当验证通过后,走jwt服务,传递user过去,jwt服务对user进行加密,返回accesstoken。 如果import 了authmodule,就别在provider里去引入服务,否则会让你再注册遍。 controller里直接调用服务:
@UseGuards ( LocalAuthGuard)
@Post ( 'auth/login' )
async login ( @Request ( ) req) {
return this . authService. login ( req. user) ;
}
这样输入正确后则返回accesstoken。 下面制作Jwt passport 新建jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt' ;
import { PassportStrategy } from '@nestjs/passport' ;
import { Injectable } from '@nestjs/common' ;
import { jwtConstants } from './constants' ;
@Injectable ( )
export class JwtStrategy extends PassportStrategy ( Strategy) {
constructor ( ) {
super ( {
jwtFromRequest: ExtractJwt. fromAuthHeaderAsBearerToken ( ) ,
ignoreExpiration: false ,
secretOrKey: jwtConstants. secret,
} ) ;
}
async validate ( payload: any ) {
return { userId: payload. sub, username: payload. username } ;
}
}
jwtFromRequest 一般都是提取bearerToken,这个是约定。 第二个忽略过期,就是过期就无效。 provider中导入它:
providers: [AuthService, LocalStrategy, JwtStrategy],
import { Injectable } from '@nestjs/common' ;
import { AuthGuard } from '@nestjs/passport' ;
@Injectable ( )
export class JwtAuthGuard extends AuthGuard ( 'jwt' ) { }
@UseGuards ( JwtAuthGuard)
@Get ( 'profile' )
getProfile ( @Request ( ) req) {
return req. user;
}
此时就完成了。 可以试下,先拿到accesstoken 然后设置请求头Authorization 值为Bearer token返回符合预期即可成功。 有可能不满足现有jwt的验证,需要额外逻辑,可以这么写:
import {
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common' ;
import { AuthGuard } from '@nestjs/passport' ;
@Injectable ( )
export class JwtAuthGuard extends AuthGuard ( 'jwt' ) {
canActivate ( context: ExecutionContext) {
return super . canActivate ( context) ;
}
handleRequest ( err, user, info) {
if ( err || ! user) {
throw err || new UnauthorizedException ( ) ;
}
return user;
}
}