parent
3763a85613
commit
da7442c3e1
@ -0,0 +1,8 @@ |
|||||||
|
import { Global, Module } from '@nestjs/common'; |
||||||
|
import { GemailService } from './gemail.service'; |
||||||
|
@Global() |
||||||
|
@Module({ |
||||||
|
providers: [GemailService], |
||||||
|
exports: [GemailService], |
||||||
|
}) |
||||||
|
export class GemailModule {} |
@ -0,0 +1,231 @@ |
|||||||
|
import { Injectable } from '@nestjs/common'; |
||||||
|
import * as nodemailer from 'nodemailer'; |
||||||
|
import config from '@CFG/index'; |
||||||
|
|
||||||
|
@Injectable() |
||||||
|
export class GemailService { |
||||||
|
// @ 邮件服务
|
||||||
|
private transporter; |
||||||
|
// @ 邮箱配置
|
||||||
|
private EMAILCONFIG; |
||||||
|
// @ 系统名称,作用于邮件主题。
|
||||||
|
private sysName: string; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.start(); |
||||||
|
} |
||||||
|
|
||||||
|
// ? ?
|
||||||
|
// ? 函数名称: start
|
||||||
|
// ? 函数描述: 生成邮件服务
|
||||||
|
// ? ?
|
||||||
|
private start() { |
||||||
|
const EMAILCONFIG = config().email; |
||||||
|
const transporter = nodemailer.createTransport({ |
||||||
|
//node_modules/nodemailer/lib/well-known/services.json 查看相关的配置,如果使用qq邮箱,就查看qq邮箱的相关配置
|
||||||
|
host: EMAILCONFIG.host, |
||||||
|
// secureConnection:true,
|
||||||
|
service: 'qq', //类型qq邮箱
|
||||||
|
// port: 465,
|
||||||
|
secure: true, // true for 465, false for other ports
|
||||||
|
auth: { |
||||||
|
user: EMAILCONFIG.user, // 发送方的邮箱
|
||||||
|
pass: EMAILCONFIG.pass, // smtp 的授权码
|
||||||
|
}, |
||||||
|
//pass 不是邮箱账户的密码而是stmp的授权码(必须是相应邮箱的stmp授权码)
|
||||||
|
//邮箱---设置--账户--POP3/SMTP服务---开启---获取stmp授权码
|
||||||
|
}); |
||||||
|
this.transporter = transporter; |
||||||
|
this.EMAILCONFIG = EMAILCONFIG; |
||||||
|
this.sysName = config().master.systemName; |
||||||
|
} |
||||||
|
|
||||||
|
// ? ?
|
||||||
|
// ? 函数名称: senMail(邮件内容, 收件人邮箱)
|
||||||
|
// ? 函数描述: 发送邮件主函数
|
||||||
|
// ? ?
|
||||||
|
private senMail(html, mail) { |
||||||
|
return new Promise((res, rej) => { |
||||||
|
const mailOptions = { |
||||||
|
from: '"TOGY | 心曲Tune" <togy.gc@qq.com>', // 发送方
|
||||||
|
to: mail, //接收者邮箱,多个邮箱用逗号间隔
|
||||||
|
subject: `${this.sysName}`, // 标题
|
||||||
|
text: 'Hello world?', // 文本内容
|
||||||
|
html, |
||||||
|
}; |
||||||
|
this.transporter.sendMail(mailOptions, (error, info) => { |
||||||
|
if (error) { |
||||||
|
rej([error, info]); |
||||||
|
} else { |
||||||
|
res(info); //因为是异步 所有需要回调函数通知成功结果
|
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
// ? ?
|
||||||
|
// ? 函数名称: sendTestEmail
|
||||||
|
// ? 函数描述: 给指定邮箱发送测试验证码
|
||||||
|
// ? ?
|
||||||
|
public sendTestEmail(email) { |
||||||
|
return new Promise(async (res, rej) => { |
||||||
|
const test = ` |
||||||
|
<style> |
||||||
|
.emailBody{ |
||||||
|
position: relative; |
||||||
|
min-height: 400px; |
||||||
|
min-width: 300px; |
||||||
|
height: 100vh; |
||||||
|
width:90vw; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
margin: 0 auto; |
||||||
|
padding: 0; |
||||||
|
overflow: overlay; |
||||||
|
} |
||||||
|
.emailMain{ |
||||||
|
position:relative; |
||||||
|
|
||||||
|
} |
||||||
|
.emailTitle{ |
||||||
|
line-height: 3em; |
||||||
|
font-size: 24px; |
||||||
|
font-weight: bold; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
.emailTitleLetter{ |
||||||
|
position:relative; |
||||||
|
font-size: 16px; |
||||||
|
line-height: 3em; |
||||||
|
} |
||||||
|
.emailCode{ |
||||||
|
position:relative; |
||||||
|
height: 100px; |
||||||
|
width: 100%; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
} |
||||||
|
.emailCode>div{ |
||||||
|
position:relative; |
||||||
|
box-sizing: border-box; |
||||||
|
padding: 1em; |
||||||
|
font-size: 30px; |
||||||
|
text-align: center; |
||||||
|
color: #fefefe; |
||||||
|
background-color: #222; |
||||||
|
} |
||||||
|
.emailDescription{ |
||||||
|
position:relative; |
||||||
|
text-align: center; |
||||||
|
padding: 20px 0 ; |
||||||
|
line-height: 1.8em; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
<div class="emailBody"> |
||||||
|
<div class="emailMain"> |
||||||
|
<div class="emailTitle">我们欢迎您使用${this.sysName}</div> |
||||||
|
<div class="emailTitleLetter">您在某些地方请求了邮箱的验证码,如果不是自己操作请修改账户的密码。</div> |
||||||
|
<div class="emailCode"> |
||||||
|
<div>ABCDEF</div> |
||||||
|
</div> |
||||||
|
<div class="emailDescription">此邮件作用于注册<br/>截止邮件发送时间5分钟内使用有效。</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
`;
|
||||||
|
try { |
||||||
|
const resd = await this.senMail(test, email); |
||||||
|
res(resd); |
||||||
|
} catch (e) { |
||||||
|
rej(e); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// ? ?
|
||||||
|
// ? 函数名称: sendRegisterCodeMail(邮箱, 验证码)
|
||||||
|
// ? 函数描述: 给制定邮箱发送注册验证码
|
||||||
|
// ? ?
|
||||||
|
public sendRegisterCodeMail(email, code) { |
||||||
|
return new Promise(async (res, rej) => { |
||||||
|
const test = ` |
||||||
|
<style> |
||||||
|
.emailBody{ |
||||||
|
position: relative; |
||||||
|
min-height: 400px; |
||||||
|
min-width: 300px; |
||||||
|
height: 100%; |
||||||
|
width:100%; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
margin: 0 auto; |
||||||
|
padding: 0; |
||||||
|
overflow: overlay; |
||||||
|
} |
||||||
|
.emailMain{ |
||||||
|
position:relative; |
||||||
|
|
||||||
|
} |
||||||
|
.emailTitle{ |
||||||
|
line-height: 3em; |
||||||
|
font-size: 24px; |
||||||
|
font-weight: bold; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
.emailTitleLetter{ |
||||||
|
position:relative; |
||||||
|
font-size: 16px; |
||||||
|
line-height: 3em; |
||||||
|
} |
||||||
|
.emailCode{ |
||||||
|
position:relative; |
||||||
|
height: 100px; |
||||||
|
width: 100%; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
} |
||||||
|
.emailCode>div{ |
||||||
|
position:relative; |
||||||
|
box-sizing: border-box; |
||||||
|
padding: 1em; |
||||||
|
font-size: 30px; |
||||||
|
text-align: center; |
||||||
|
color: #fefefe; |
||||||
|
background-color: #222; |
||||||
|
} |
||||||
|
.emailDescription{ |
||||||
|
position:relative; |
||||||
|
text-align: center; |
||||||
|
padding: 20px 0 ; |
||||||
|
line-height: 1.8em; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
<div class="emailBody"> |
||||||
|
<div class="emailMain"> |
||||||
|
<div class="emailTitle">我们欢迎您使用${this.sysName}</div> |
||||||
|
<div class="emailTitleLetter">您在某些地方请求了邮箱的验证码,如果不是自己操作请修改账户的密码。</div> |
||||||
|
<div class="emailCode"> |
||||||
|
<div>${code}</div> |
||||||
|
</div> |
||||||
|
<div class="emailDescription">此邮件作用于账户注册<br/>截止邮件发送时间5分钟内使用有效。</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
`;
|
||||||
|
try { |
||||||
|
console.time('EMAIL'); |
||||||
|
const resd = await this.senMail(test, email); |
||||||
|
console.timeEnd('EMAIL'); |
||||||
|
res(resd); |
||||||
|
} catch (e) { |
||||||
|
rej({ |
||||||
|
data: e, |
||||||
|
message: '发送邮件失败!', |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
import { Global, Module } from '@nestjs/common'; |
||||||
|
import { GtoolsService } from './gtools.service'; |
||||||
|
@Global() |
||||||
|
@Module({ |
||||||
|
providers: [GtoolsService], |
||||||
|
exports: [GtoolsService], |
||||||
|
}) |
||||||
|
export class GtoolsModule {} |
@ -0,0 +1,137 @@ |
|||||||
|
import { Injectable } from '@nestjs/common'; |
||||||
|
import * as crypto from 'crypto'; |
||||||
|
import config from '@CFG/index'; |
||||||
|
import * as jwt from 'jsonwebtoken'; |
||||||
|
|
||||||
|
// @ 不可逆加密
|
||||||
|
enum HASHT { |
||||||
|
MD5 = 'md5', // 32位
|
||||||
|
SHA256 = 'sha256', // 64位
|
||||||
|
SHA512 = 'sha512', // 128位
|
||||||
|
} |
||||||
|
// @ 可逆加密
|
||||||
|
enum AEST { |
||||||
|
AES128 = 'aes-128-cbc', |
||||||
|
AES256 = 'aes-256-gcm', |
||||||
|
} |
||||||
|
|
||||||
|
@Injectable() |
||||||
|
export class GtoolsService { |
||||||
|
// @ 加解密配置文件
|
||||||
|
private readonly encryptionCFG = config().encryption; |
||||||
|
private readonly tokenCFG = config().token; |
||||||
|
|
||||||
|
// @ AES-128-cec加解密 iv和key
|
||||||
|
private AES128CEC_key; |
||||||
|
private AES128CEC_iv; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.AES128CEC_key = Buffer.from( |
||||||
|
this.makeHASH(this.encryptionCFG.secretKey, HASHT.MD5).slice(-16), |
||||||
|
'utf8', |
||||||
|
); |
||||||
|
|
||||||
|
this.AES128CEC_iv = Buffer.from( |
||||||
|
this.makeHASH(this.encryptionCFG.salt, HASHT.MD5).slice(-16), |
||||||
|
'utf8', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// ? ?
|
||||||
|
// ? 函数名称: getRandomString(长度,大小写默认big)
|
||||||
|
// ? 函数描述: 获取制定长度的随机字符串
|
||||||
|
// ? ?
|
||||||
|
public getRandomString(len: number, size = 'big'): string { |
||||||
|
if (size == 'big') { |
||||||
|
return Math.random() |
||||||
|
.toString(36) |
||||||
|
.slice(-len) |
||||||
|
.toString() |
||||||
|
.toUpperCase(); |
||||||
|
} else { |
||||||
|
return Math.random() |
||||||
|
.toString(36) |
||||||
|
.slice(-len) |
||||||
|
.toString() |
||||||
|
.toLowerCase(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ? ?
|
||||||
|
// ? 函数名称: makeUUID
|
||||||
|
// ? 函数描述: 生成UUID
|
||||||
|
// ? ?
|
||||||
|
public makeUUID() { |
||||||
|
return crypto.randomUUID().split('-').join('').toUpperCase(); |
||||||
|
} |
||||||
|
|
||||||
|
// ? ?
|
||||||
|
// ? 函数名称: makeHASH(加密内容, 加密编码默认为SHA512)
|
||||||
|
// ? 函数描述: =加密字符串
|
||||||
|
// ? ?
|
||||||
|
public makeHASH(plaintext, algorithm = HASHT.SHA512) { |
||||||
|
const sha512 = crypto.createHash(algorithm); |
||||||
|
const sha512Sum = sha512.update(plaintext + this.encryptionCFG.salt); |
||||||
|
const ciphertext = sha512Sum.digest('hex'); |
||||||
|
return ciphertext; |
||||||
|
} |
||||||
|
|
||||||
|
//#region 可逆加密
|
||||||
|
// ? ?
|
||||||
|
// ? 函数名称: encrypt
|
||||||
|
// ? 函数描述: 加密字符串
|
||||||
|
// ? ?
|
||||||
|
encrypt(plaintext, algorithm = AEST.AES128) { |
||||||
|
const cipher = crypto.createCipheriv( |
||||||
|
algorithm, |
||||||
|
this.AES128CEC_key, |
||||||
|
this.AES128CEC_iv, |
||||||
|
); // 初始化加密算法
|
||||||
|
let ciphertext = cipher.update(plaintext, 'utf8', 'hex'); |
||||||
|
ciphertext += cipher.final('hex'); |
||||||
|
return ciphertext; |
||||||
|
} |
||||||
|
|
||||||
|
// ? ?
|
||||||
|
// ? 函数名称: decrypt
|
||||||
|
// ? 函数描述: 解密字符串
|
||||||
|
// ? ?
|
||||||
|
decrypt(ciphertext, algorithm = AEST.AES128) { |
||||||
|
let plaintext = ''; |
||||||
|
const cipher = crypto.createDecipheriv( |
||||||
|
algorithm, |
||||||
|
this.AES128CEC_key, |
||||||
|
this.AES128CEC_iv, |
||||||
|
); |
||||||
|
plaintext += cipher.update(ciphertext, 'hex', 'utf8'); |
||||||
|
plaintext += cipher.final('utf8'); |
||||||
|
return plaintext; |
||||||
|
} |
||||||
|
//#endredion
|
||||||
|
|
||||||
|
//#region jwt
|
||||||
|
createToken(data) { |
||||||
|
const plaintextToken = jwt.sign(data, this.tokenCFG.secretKey, { |
||||||
|
expiresIn: this.tokenCFG.timeout / 1000, |
||||||
|
}); |
||||||
|
const ciphertextToken = this.encrypt(plaintextToken); |
||||||
|
return ciphertextToken; |
||||||
|
} |
||||||
|
resolveToken(ciphertextToken) { |
||||||
|
try { |
||||||
|
// 解析密文
|
||||||
|
const plaintextToken = this.decrypt(ciphertextToken); |
||||||
|
const data = jwt.verify(plaintextToken, this.tokenCFG.secretKey); |
||||||
|
return { |
||||||
|
token: true, |
||||||
|
data, |
||||||
|
}; |
||||||
|
} catch (e) { |
||||||
|
return { |
||||||
|
token: false, |
||||||
|
data: e, |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
//#endredion
|
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
/* * |
||||||
|
* * @name: signIn.dto.ts |
||||||
|
* * @description: 登陆部分的数据传输验证 |
||||||
|
* * @author: xi |
||||||
|
* * @date: 2023/3/26 22:41 |
||||||
|
* * */ |
||||||
|
|
||||||
|
import { Length } from 'class-validator'; |
||||||
|
|
||||||
|
export class SignInPasswdEntryDto { |
||||||
|
// @ 用户名
|
||||||
|
@Length(8, 128, { message: '请将用户名长度控制在8到128位之间!' }) |
||||||
|
username: string; |
||||||
|
|
||||||
|
// @ 密码
|
||||||
|
@Length(8, 128, { message: '请将密码长度控制在8到128位之间!' }) |
||||||
|
password: string; |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
import { |
||||||
|
CanActivate, |
||||||
|
ExecutionContext, |
||||||
|
Injectable, |
||||||
|
UnauthorizedException, |
||||||
|
} from '@nestjs/common'; |
||||||
|
import { Observable } from 'rxjs'; |
||||||
|
import { GloggerService } from '@/Gservice/GLOGGER/glogger.service'; |
||||||
|
import { GtoolsService } from '@/Gservice/GTOOLS/gtools.service'; |
||||||
|
// 此文件为守卫
|
||||||
|
@Injectable() |
||||||
|
export class VerifyGuard implements CanActivate { |
||||||
|
constructor( |
||||||
|
private readonly logger: GloggerService, |
||||||
|
private readonly tools: GtoolsService, |
||||||
|
) {} |
||||||
|
canActivate( |
||||||
|
context: ExecutionContext, |
||||||
|
): boolean | Promise<boolean> | Observable<boolean> { |
||||||
|
const request = context.switchToHttp().getRequest(); |
||||||
|
const url = request.url; |
||||||
|
// this.logger.info(url);
|
||||||
|
const token = request.headers.authorization; |
||||||
|
// this.logger.info(token);
|
||||||
|
const data = this.tools.resolveToken(token); |
||||||
|
// this.logger.info(data);
|
||||||
|
if (data.token) { |
||||||
|
context.switchToHttp().getRequest().user = '32'; |
||||||
|
// this.logger.info(request.user);
|
||||||
|
} |
||||||
|
// console.log(request);
|
||||||
|
// console.log(request.url);request.headers
|
||||||
|
// this.logger.info('GUARG', request.url, "TEST");
|
||||||
|
// 自定义返回消息
|
||||||
|
// throw new UnauthorizedException({
|
||||||
|
// statusCode: 403,
|
||||||
|
// message: '你没有访问权限',
|
||||||
|
// error: 'Forbidden',
|
||||||
|
// });
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue