最近在学习nestjs,在学习使用验证码登录设置验证码session的值,遇到的问题以及是怎样解决
前端登录页面
<!--
* @Description:
* @Author: HYH
* @LastEditors: HYH
* @LastEditTime: 2023-06-08 10:52:05
-->
<template>
<main class="w-screen h-screen flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<el-form
status-icon
size="large"
ref="formRef"
label-position="top"
:model="loginForm"
:rules="rules"
class="bg-white/40 rounded-2xl shadow-2xl p-7 pt-10">
<el-form-item>
<div class="w-full font-bold text-[30px] text-center text-bg">登 录</div>
</el-form-item>
<el-form-item prop="email" label="邮箱">
<el-input v-model="loginForm.email" />
</el-form-item>
<el-form-item prop="password" label="密码">
<el-input v-model="loginForm.password" type="password" />
</el-form-item>
<el-form-item class="captcha" prop="captcha" label="验证码">
<el-input v-model="loginForm.captcha" placeholder="请输入">
<template #append>
<img
style="width: 150px"
:src="getCaptchaUrl"
alt=""
@click="(e:any)=>e.target.src = getCaptchaUrl + `?timeStamp=${Date.now()}`" />
</template>
</el-input>
</el-form-item>
<el-form-item>
<div class="flex w-full justify-between">
<div><input type="checkbox" :checked="checked" /><span class="ml-2">记住密码</span></div>
<a href="#resetPwd" class="text-[#1e80ff] font-bold">忘记密码?</a>
</div>
</el-form-item>
<el-form-item>
<el-button class="w-full !text-lg" color="#626aef" @click="login">login</el-button>
</el-form-item>
<el-form-item>
<div class="text-sm text-gray-500">
没有账号?
<a href="#regist" class="font-semibold leading-6 text-indigo-600"> 点击注册 </a>
</div>
</el-form-item>
</el-form>
</div>
</main>
</template>
<script setup lang="ts">
import useKeyDown from '@/hooks/useKeyDown'
import useValidate from '@/hooks/useValidate'
import { http } from '@/http'
import userStore from '@/store/user'
import { ElMessage, FormInstance } from 'element-plus'
import { useRouter } from 'vue-router'
const store = userStore()
const router = useRouter()
const getCaptchaUrl = ref(import.meta.env.VITE_BASE_URL + import.meta.env.VITE_BASE_API + '/login/captcha')
const formRef = ref<FormInstance>()
const loginForm = reactive({
email: '',
password: '',
captcha: '',
})
const rules = reactive<any>({
email: useValidate.email,
password: useValidate.password,
captcha: useValidate.pleaseInput,
})
const checked = ref(true)
const login = () => {
formRef.value.validate((valid) => {
if (valid) {
http.post<UserModel>('/api/login', loginForm).then((res) => {
const { code, data } = res
if (code === 200) {
store.setInfo(data)
router.push({ name: 'dashboard', replace: true })
}
})
}
})
}
useKeyDown((e) => {
e.key == 'Enter' && login()
})
onMounted(() => {})
</script>
<style lang="scss" scoped>
:deep(.captcha) {
cursor: pointer;
.el-form-item__content {
.el-input-group__append {
padding: 0;
}
}
}
</style>
vite 配置 (如果不配置proxy会提示跨域问题,试了一下服务器配置,发现不生效)
/*
* @Description:
* @Author: HYH
* @LastEditors: HYH
* @LastEditTime: 2023-07-28 20:08:45
*/
import path, { resolve } from 'path'
import fs from 'fs'
import { defineConfig, loadEnv, searchForWorkspaceRoot } from 'vite'
import usePlugin from './vite/usePlugin'
// @ts-ignore
export default defineConfig(({ command, mode, ssrBuild }) => {
/**根据 "mode" 值 读取获取本地环境变量中的.env.[mode]中 VITE_BASE_URL 的值 */
const env = loadEnv(mode, process.cwd())
console.log({
mode,
command,
})
const isProduction = mode === 'production'
return {
// 生产或开发环境下的基础路径
// base: '/doc',
// 需要用到的插件数组
plugins: usePlugin(isProduction),
server: {
proxy: {
'/api': {
target: env.VITE_BASE_URL + '/api',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
// https: {
// cert: fs.readFileSync(path.join(__dirname, './keys/cert.crt')),
// key: fs.readFileSync(path.join(__dirname, './keys/cert.key')),
// },
host: true,
port: env.VITE_FRONT_PORT,
/**可读取的文件夹 */
fs: {
allow: [searchForWorkspaceRoot(process.cwd()), '../app/enum'],
},
},
//
resolve: {
// 设置文件目录别名
alias: {
'@': resolve(__dirname, './src'),
'~': resolve(__dirname, './'),
'@@': resolve(__dirname, '../server'),
},
extensions: ['.js', '.ts', '.tsx', '.jsx'],
//
},
build: {
// outDir: path.join(__dirname, '../docBuild/koa_ts_server_full_doc'),
emptyOutDir: true,
rollupOptions: {
output: {
chunkFileNames: 'js/[name]-[hash].js', // 引入文件名的名称
entryFileNames: 'js/[name]-[hash].js', // 包的入口文件名称
assetFileNames: '[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片等
/**
* @description 清除文件名格式不正确问题
* @param name
* @returns
*/
// https://github.com/rollup/rollup/blob/master/src/utils/sanitizeFileName.ts
sanitizeFileName(name) {
// eslint-disable-next-line no-control-regex
const INVALID_CHAR_REGEX = /[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g
const DRIVE_LETTER_REGEX = /^[a-z]:/i
const match = DRIVE_LETTER_REGEX.exec(name)
const driveLetter = match ? match[0] : ''
// A `:` is only allowed as part of a windows drive letter (ex: C:\foo)
// Otherwise, avoid them because they can refer to NTFS alternate data streams.
let file = driveLetter + name.slice(driveLetter.length).replace(INVALID_CHAR_REGEX, '')
if (file.startsWith('_')) file = file.slice(1) /**去除首位字符串为 "_" , fix github page访问不了的问题*/
return file
},
// // 最小化拆分包
// manualChunks(id) {
// if (id.includes('node_modules')) {
// return id.toString().split('node_modules/')[1].split('/')[0].toString()
// }
// },
},
},
},
esbuild: {
// drop: isProduction ? ['console', 'debugger'] : [], // 删除 所有的console 和 debugger
},
}
})
后端nestjs
import { Body, Controller, Get, Post, Res, Session } from '@nestjs/common';
import { Response } from 'express';
import { LoginService } from './login.service';
import { ApiTags } from '@nestjs/swagger';
// import { LoginPipe } from './login.pipe';
@Controller('login')
@ApiTags('登录接口')
export class LoginController {
constructor(private readonly loginService: LoginService) {}
@Post()
login(@Body() dto, @Session() session) {
const { captcha } = dto;
if (captcha != session.captcha) {
return '验证码错误';
}
// return this.loginService.login(dto);
}
@Get('captcha')
captcha(@Res() res: Response, @Session() session) {
const captcha = this.loginService.captcha();
session.captcha = captcha.text;
res.type('image/svg+xml');
res.send(captcha.data);
}
}
问题复现
使用localhost打开项目,一直拿不到session
改成ip之后就可以了