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