知識點
- jwt身份認證
- md5加密
- typeorm事務(transaction)的使用
本文會延續上一篇文章,繼續實現login功能,並實現API的身份認證,查看全部源碼。
JWT身份認證
對與絕大多數應用程序來說,身份認證是必不可少的組成部分,而對用戶的身份認證授權的策略和方法非常多,選用何種方法取決於項目的需求。
passport是node.js中的一個比較流行的認證庫,本項目Demo會使用passport-jwt策略來實現用戶身份認證。
JWT(Json Web Token)是一種用於雙方之間傳遞安全信息的簡潔的、URL安全的表述性聲明規范。JWT作為一個開放的標准(RFC 7519),定義了一種簡潔的,自包含的方法用於通信雙方之間以Json對象的形式安全的傳遞信息。因為數字簽名的存在,這些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘鑰對進行簽名。
安裝
npm install --save @nestjs/passport passport passport-jwt jsonwebtoken
添加jwt.stratagy.ts:
import { ExtractJwt, Strategy } from 'passport-jwt';
import { AuthService } from './auth.service';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtPayload } from './jwt-payload.interface'
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authService: AuthService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
passReqToCallback: true,
secretOrKey: 'secretKey',
});
}
async validate(payload: JwtPayload, done: Function) {
console.log('entered jwt')
const user = await this.authService.validateUser(payload.userNmae);
if (!user) {
return done(new UnauthorizedException(), false);
}
done(null, user);
}
}
通過validate()方法獲取token然后傳遞給auth.service進行驗證。
添加autn.service.ts:
import { Injectable } from '@nestjs/common'
import { Repository } from 'typeorm';
import { JwtPayload } from './jwt-payload.interface'
import * as jwt from 'jsonwebtoken';
import { Employee } from '../entities/employee.entity'
import { InjectRepository } from '@nestjs/typeorm';
@Injectable()
export class AuthService {
user: Employee
constructor(
@InjectRepository(Employee)
private readonly employeeRepository: Repository<Employee>) { }
async createToken(userName: string, passwoerd: string): Promise<any> {
const user: JwtPayload = { userNmae: userName, passwoerd: passwoerd }
return jwt.sign(user, 'secretKey', { expiresIn: 3600 });
}
async validateUser(name: string): Promise<any> {
return this.employeeRepository.findOne({ name: name });
}
async findEmployeeByName(name: string): Promise<Employee> {
return this.employeeRepository.findOne({ name: name });
}
getUser(): Employee {
return this.user;
}
async login(name: string, password: string): Promise<any> {
this.user = await this.employeeRepository.findOne({ name: name });
if (this.user != undefined && this.user.password == password) {
return this.createToken(this.user.name, this.user.password);
} else {
return 'login failed !'
}
}
}
在auth.service中,createToken()用來生成Token信息,validateUser()驗證身份信息,login用於用戶登錄,在login中先根據用戶名查詢用戶驗證密碼,然后生成Token返回給前端。這里在生成token是指定了到期時間和secretkey.
auth.controller.ts:
import { Controller, Get, Param, UseGuards, HttpStatus, HttpCode } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from '@nestjs/passport';
import { callback } from './jwt.strategy'
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) { }
@Get('login')
@HttpCode(HttpStatus.OK)
async login(@Param() params): Promise<any> {
return this.authService.login(params.name, params.password);
}
@Get('checklogin')
@UseGuards(AuthGuard('jwt', { session: false, callback }))
//@UseGuards(new RoleGuard(['admin']))
public checkLogin() {
return "valid user:" + this.authService.getUser().name;
}
}
auth.controller中checklogin在訪問時,使用passport的UserGuard配置jwt策略來驗證身份信息,並在驗證完成后指定調用callback函數。
MD5加密
本Demo使用了一個比較簡單的加密策略,MD5。
安裝包:
npm install --save @types/crypto-js crypto-js
加密過程也比較簡單
import * as crypto from 'crypto-js'
employee.password = crypto.MD5('123').toString();
typeorm 事務的使用(transaction)
事務在srvice中是比較常見的應用場景,在typeorm的官方文檔中提供了多種方法來進行事務管理,本文介紹兩種基本的使用方法。
1.getManager(隱式commit,隱式rollback)
async edit(): Promise<string> {if (employee) { return getManager().transaction(async transactionalEntityManager => { await transactionalEntityManager.update<Employee>(Employee, { name: 'novak' }, { age: 23 }); await transactionalEntityManager.delete<Company>(Company, { id: 10 }); let a = '123bew'; console.log(a[10].length);//制造異常 }).then(res => { return 'tranction done' }).catch(Error => { return 'tranction failed, ' + Error; }) } else { return 'employee not found'; } }
使用getManager().transaction來創建事務模塊,為了驗證效果,本文特意寫了一個異常語句。驗證結果是:出現異常后事務會自動回滾;如果沒有異常,事務自動提交。
2.queryRunner(顯式commit,顯式rollback)
async editUseQueryRunner(): Promise<string> { let employee = await this.employeeRepository.findOne({ name: "novak" }); console.log(employee) if (employee) { const connection = getConnection(); const queryRunner = connection.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { await queryRunner.manager.update<Employee>(Employee, { name: 'novak' }, { age: 24 }); /* let a = '123bew'; console.log(a[10].length); */ await queryRunner.commitTransaction(); return 'transaction done' } catch (err) { await queryRunner.rollbackTransaction(); return 'transaction failed' } } else { return 'employee not found' } }
從代碼中就可以看到queryRunner是顯式的提交和回滾事務的。
