authentication
快速创建Controller、Service、Module、DTO文件命令
nest g resource user
用户注册
用户注册时的密码如果直接存储明文到数据库中会有安全问题,这里我们使用bcryptjs进行加密,把加密后的用户密码再存储到数据库中。
bcryptjs是一个用于密码散列化的JavaScript库。它使用bcrypt算法对密码进行散列,从而增强密码的安全性,减少被破解的风险。bcrypt算法使用salt值来增强密码的安全性,salt值随机生成并和密码一起进行散列,这使得破解的难度大大增加。
bcryptjs库使用简单,它提供了两个主要的函数:genSalt和hash。通过genSalt函数生成一个salt值,然后使用hash函数将密码和salt值一起进行散列。bcryptjs还提供了一个compare函数,可以用于检查给定的密码和散列后的密码是否匹配。
安装:
pnpm install bcryptjs
常用方法:
/*处理加密
bcryptjs.hashSync(data,salt)
-data 要加密的数据
-salt 使用哈希加密的salt,若指定为数字,则将指定的轮数生成盐并使用
*/
const hashpass=bcryptjs.hashSync(pass,10)
/*校验数据
bcryptjs.compareSync(data,encrypted)
-data 要进行比较的数据,登录后前端传递过来的数据
-encrypted 数据库中查询出来的加密后的密码
*/
const ispass=bcryptjs.compareSync(pass,encryptPass)
我们使用auth users两个module来管理用户登录注册逻辑,其中auth进行接口跳转处理,user进行具体逻辑处理
接下来进行entity和dto的处理:
user entity
import { BeforeInsert, Column,Entity, PrimaryGeneratedColumn } from "typeorm";
import * as bcrypt from 'bcryptjs';
@Entity('user')
export class User{
@PrimaryGeneratedColumn('uuid') //使用uuid为每一位用户生成独立唯一的id
id:number;
@Column({length:100})
username:string;
@Column({length:100})
password:string;
@Column({default:null})
avatar:string;
@BeforeInsert()
async hashPassword(){
if(!this.password) return
//加密密码
this.password=await bcrypt.hashSync(this.password,10)
}
}
dto
-
create-user.dto
import { IsNotEmpty } from "class-validator"; export class CreateUserDto { @IsNotEmpty({message:"请输入用户名"}) username:string; @IsNotEmpty({message:"请输入密码"}) password:string; } update-user.dto继承即可,user-info按照其他信息填充
user.services
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
//注册
async register(createUser: CreateUserDto) {
const { username } = createUser;
const existUser = await this.userRepository.findOne({
where: { username },
});
if(existUser){
throw new HttpException("用户名已存在", HttpStatus.BAD_REQUEST)
}
const newUser = await this.userRepository.create(createUser)
return await this.userRepository.save(newUser);
}
}
user.controller
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post('register')
register(@Body() createUser: CreateUserDto) {
return this.userService.register(createUser);
}
}
user.module
别忘记在module中添加:
@Module({
imports:[TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService]
})
export class UserModule {}
密码不返回前端
使用post方法创建用户成功后,会返回相应创建成功的信息,为了安全性,后端在返回这些信息中不应包含密码,可以在返回数据的时候处理,不返回密码给前端:
-
在user.entity给密码加上装饰器@Exclude
@Exclude() //排除密码字段 @Column({length:100}) password:string; -
在user.controller中接口方法处加上拦截器
@UseInterceptors(ClassSerializerInterceptor) @Post('register') register(@Body() createUser: CreateUserDto) { return this.userService.register(createUser); }这样在返回的字段中就不会包含密码啦~
通过postcode测试user/register通过json输入test123,可以看到测试成功
用户登录
用户注册与用户登录流程差异不大,只不过多了一个使用jwt验证的流程
JWT是指JSON Web Token,是一种用于在网络应用之间传递信息的安全标准。它是一种基于JSON的开放标准(RFC 7519),用于在不同实体之间安全地传输信息。
JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。头部包含了令牌的类型和所使用的算法,载荷包含了需要传输的信息,签名用于验证数据的完整性。
JWT的工作流程如下:
- 用户进行身份认证后,服务器生成一个JWT,并将其返回给用户。
- 用户在之后的请求中将JWT作为身份验证的方式,放在请求的头部、查询字符串或请求体中。
- 服务器接收到请求后,解析JWT并验证其有效性和完整性。
- 如果验证成功,服务器会根据JWT中的信息进行相应操作。
使用JWT的好处包括:
- 无需在服务器端存储会话信息,减轻服务器的存储压力。
- 客户端可以将JWT保存在本地,减少了对服务器的频繁请求。
- JWT使用数字签名进行验证,可以确保数据的完整性和安全性。
需要注意的是,JWT中的信息是可以被解码的,但是不能被修改,因为由签名保证了数据的完整性。
user.service
Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
private jwtService: JwtService //引入JwtService
) {}
//登录
async login(createUser:CreateUserDto){
const { username,password } = createUser;
const payload = { username };
const existUser = await this.userRepository.findOne({
where: { username }
}); //验证密码是否正确
if(existUser && await bcrypt.compare(password,existUser.password)){
return {
...existUser,
accessToken:this.jwtService.sign(payload)
}
}
throw new HttpException("用户名或密码错误", HttpStatus.BAD_REQUEST)
}
user.controller
添加登录接口
@UseInterceptors(ClassSerializerInterceptor)
@Post('login')
login(@Body() createUser: CreateUserDto){
return this.userService.login(createUser)
}
user.module
@Module({
imports:[
TypeOrmModule.forFeature([User]),
JwtModule.register({ //引入JWT模块
secret:process.env.jwt_secret,
signOptions:{expiresIn:'1d'}
})],
controllers: [UserController],
providers: [UserService,JwtService]
})