1、npm下载需要的包
安装session包
npm i express-session
npm i -D @types/express-session安装验证码包
//安装包
npm i svg-captcha
//安装ts代码提示
npm i -D @types/@svg-captcha
2、定义session
Session(会话)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as session from 'express-session';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(
session({
secret: 'my-secret',
resave: false,
saveUninitialized: false,
rolling: true, //在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)
}),
);
app.setGlobalPrefix('api');
await app.listen(3000);
}
bootstrap();
3、实现验证码接口
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Session,
} from '@nestjs/common';
import * as svgCaptcha from 'svg-captcha';
......
@Get('captchaImage')
getCode(@Session() session) {
const captcha = svgCaptcha.create({
size: 4, //验证码长度
fontSize: 50,
width: 110,
height: 38,
background: '#cc9966', //背景颜色
});
session.code = captcha.text; //session保存验证码
return { img: captcha.data }; // aptcha.data 返回的是svg图
}
4、前端请求获取到验证码
<template>
<div class="login-code">
<!-- <img :src="codeUrl" @click="getCode" class="login-code-img" /> -->
<div @click="getCode" v-html="codeUrl" class="login-code-img"></div>
</div>
</template>
<script>
import request from '@/utils/request'
export default {
name: '',
data() {
return { codeUrl: '' }
},
created() {
this.getCode()
},
methods: {
getCode() {
request({
url: '/login/captchaImage',
headers: {
isToken: false
},
method: 'get',
timeout: 20000
}).then((res) => {
this.codeUrl = res.img
})
}
}
}
</script>
<style>
</style>
5、验证验证码(登录接口)
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Session,
} from '@nestjs/common';
import * as svgCaptcha from 'svg-captcha';
@Controller('login')
export class LoginController {
constructor(private readonly loginService: LoginService) {}
// 获取验证码
@Get('captchaImage')
getCode(@Session() session) {
const captcha = svgCaptcha.create({
size: 4, //验证码长度
fontSize: 50,
width: 110,
height: 38,
background: '#cc9966', //背景颜色
});
session.code = captcha.text; //session保存验证码
return { img: captcha.data }; // aptcha.data 返回的是svg图
}
//登录接口
@Post('signUp')
signUp(@Session() session, @Body() body) {
// 前端传回来的验证码,转换成小写
const code = body.code.toLowerCase();
//get方式获取的验证码定义的
const sessionCode = String(session.code).toLowerCase();
console.log(code, sessionCode);
if (sessionCode === code) {
return {
code: 200,
msg: '验证码正确',
data: {},
};
} else {
return {
code: 601,
msg: '验证码错误',
data: {},
};
}
}
}
注意:如果你是使用core来解决跨域问题,这时就会出现登录接口的session上没有保存之前验证码获取的code这个属性。
原因:
第一次接到请求,后关会生成一个sessionid来标识当前会话(我使用express-session来实现),并通过set-cookie响应头在客户端生一个cookie,大概长这样,
connect.sid=s%3Au9xG34DBU1vOVbIpCax0neMxL_Uc1fIC.4ndNJL5G%2B41DtUSLbQ%2BW75Z9wduOAON4lfu2JGTDe5
由于cookie会自动发送给服务器,所以当前用户后面的所有请求都会携带这个sessionid给服务器,服务器通过这个sessionid来标识是否为同一个用户,
但是默认情况下,标准的跨域请求是不会发送cookie等用户认证凭据的,同样,后端通过Set-Cookie在跨域时默认是被浏览器忽略的,所以这个登录接口的session跟之前验证码获取接口的session是不同的。
解决办法:
方法一
前端发起请求时设置 withCredentials:true 请求头
设置该请求头后,后端Access-Control-Allow-Origin的值不能设置为 *,必须设置为具体域名
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000,
withCredentials: true, // 设置跨域,是否提供凭据信息(cookie、HTTP认证及客户端SSL证明等),且服务端要设置Access-Control-Allow-Origin,告诉浏览器允许跨域,而且这个值必须指定域名,不能设置为*
})
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as session from 'express-session';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
// 定义了被允许的请求头列表。这里指定了几个常见的请求头(如果请求头里面的自定义字段也要加上去)
allowedHeaders: [
'Accept',
'Accept-Version',
'Content-Type',
'Api-Version',
'Origin',
'X-Requested-With',
'Authorization',
'istoken', // 添加istoken字段
],
//定义了允许跨域访问的来源。可以是一个字符串,也可以是一个字符串数组
origin: ['http://localhost:8080'],
// 指定了是否允许跨域请求携带认证信息,如 Cookies、Authorization 等
credentials: true,
// 定义了响应头中可以被客户端读取的字段列表。这里只列出了一个字段名 API-Token-Expiry。
exposedHeaders: ['API-Token-Expiry'],
});
app.use(
session({
secret: 'my-secret',
resave: false,
saveUninitialized: false,
rolling: true, //在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)
}),
);
app.setGlobalPrefix('api');
await app.listen(3000);
}
bootstrap();
方法二
使用vue的代理来解决跨域就没有以上的那些事。。。