携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
本系列文章将涉及到的技术有Nest.js、Typescript、Vite、Vue、Antdv、MySql。本人也是个菜鸡前端,初涉全栈,第一次写文,希望各位大大多多包涵,有什么建议可以提出来互相交流。
-【业务场景】
角色目前主要就2种,管理员和员工。我准备做一个简单一些的登录和权限功能,登录后查询用户当前的角色,前端就通过角色编码去加载相应的菜单,之后再给管理员加一个菜单可以添加员工账号,这样就不需要劳烦我们开发了。先来实现登录验证
-【验证用户信息】
登录过程是输入用户名密码->验证数据库->判断是否成功 那么我们需要先实现一个验证的接口,很简单就是通过前端传过来的用户名和密码去数据库中查询是否存在,代码如下
import { Injectable } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { User } from 'src/entity/user.entity';
@Injectable()
export class AuthService {
constructor(private entityManager: EntityManager) {}
checkUser(user: any): Promise<User> {
const repository = this.entityManager.getRepository(User);
const res = repository.findOne({
where: {
username: user.username,
password: user.password,
},
});
return res;
}
}
// auth.controller
@Get('checkUser')
async checkUser(@Request() req: any) {
console.log(req.query);
const { username, password } = req.query;
const result: any = await this.authService.checkUser({
username,
password,
});
return result;
}
Nest的接口路由会根据控制器和请求类型装饰器描述来生成,以上对应的接口地址就是/auth/checkUser
调用之后就会返回{"id":1,"companyId":1,"username":"admin","password":"123456","name":"管理员","remark":null}
这就是我们的用户信息了,当然前提是数据库里有这样的一条数据。
-【搭建前端】
接下来我们创建一个前端项目,直接使用vite创建一个vue的项目,并且添加vue路由,antdv, axios,新建一个登录页面
$ yarn create vite
$ yarn add vue-router ant-design-vue axios
前端跑起来后,随便写个登录界面,点击登录按钮直接调用接口
axios.get('http://localhost:3000/auth/checkUser',{
params: {
username: formState.username,
password: formState.password
}
}).then(r=>{
console.log(r)
})
这个时候发现跨域了,无法访问,可以暂时把Nest的跨域限制放开
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors(); // 允许跨域
await app.listen(3000);
}
打开跨域后就能请求到用户信息了。
到了这一步,会发现我们的程序还不完善,会有一些小问题,
- 没有数据的时候返回的是空白没有报错
- 密码是保存的明文
- 登录后的状态我们还没有记录
第一个问题,我们在后端加上一个判断异常处理就好了,Nest提供了HHttpException这样一个类,可以很方便的抛出一个错误,前端直接在axios的catch里就能捕获得到错误信息。
if (!result) {
throw new HttpException('用户验证失败', HttpStatus.UNAUTHORIZED);
}
第二个问题,我们引入一个库bcrypt
,是专门用来加密的,他主要有2个方法 hashSync
用来加密,compareSync
用来比对原文和加密后是否一致,具体用法参考下面
$ yarn add bcrypt
安装后修改User的实体类
import * as bcryptjs from 'bcryptjs';
@Entity('user')
export class User {
// 此处省略
@BeforeInsert()
async encryptPwd() {
`/**`
` ``* 加密处理 - 同步方法`
` ``* bcryptjs.hashSync(data, salt)`
` ``* - data 要加密的数据`
` ``* - slat 用于哈希密码的盐。如果指定为数字,则将使用指定的轮数生成盐并将其使用。推荐 10`
` ``*/`
this.password = await bcryptjs.hashSync(this.password, 10);
}
}
Typeorm 有提供 订阅者和监听者装饰器 在这种时候就显得特别方便好用
我们需要再添加一个注册的接口,
// auth.service
register(user: any): Promise<InsertResult> {
const repository = this.entityManager.getRepository(User);
const userm = new User();
userm.username = user.username;
userm.password = user.password;
const res = repository.insert(userm);
return res;
}
// auth.controller
@Get('register')
async register(@Request() req: any) {
console.log(req.query);
const { username, password } = req.query;
const result: any = await this.authService.register({
username,
password,
});
return result;
}
前端界面添加一个注册按钮,这个时候我们来试一试效果
这样就注册成功了,接着我们要把验证登录那个地方的判断也给加上,
@Get('checkUser')
async checkUser(@Request() req: any) {
console.log(req.query);
const { username, password } = req.query;
const result: any = await this.authService.checkUser({
username,
});
`/**`
` ``* 校验 - 使用同步方法`
` ``* bcryptjs.compareSync(data, encrypted)`
` ``* - data 要比较的数据, 使用登录时传递过来的密码`
` ``* - encrypted 要比较的数据, 使用从数据库中查询出来的加密过的密码`
` ``*/`
const isOk = bcryptjs.compareSync(password, result.password);
if (!isOk) {
throw new HttpException('用户验证失败', HttpStatus.UNAUTHORIZED);
}
return result;
}
第三个问题,我们得采取保存登录信息的机制,一般来说有这么几种:cookie,session,token,还有最近流行的jwt,也就是json web token.
那么下一篇,我们就实现jwt认证。
这部分完整代码可以看我的github地址:点这里,点这里,点这里