目录
一. 购买
没有企业资质,个人想要调用阿里云短信服务,我的解决办法是购买其他公司已经开通的阿里云短信服务。
1. 在阿里云首页搜索框搜索“短信验证码”
2. 选择其中一个并购买
我选择的是这个,有提供免费次数,价格相对比较实惠
链接:【体验价2分5】三网短信接口-短信验证码-通知短信-短信验证码接口-三网短信接口-短信通知-短信验证码-验证码短信-服务短信【最新版】_商业智能_电商_金融-云市场-阿里云
购买成功后可以在 已购买的服务【阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台】 中看到短信验证码的AppKey、AppSecret、AppCode这些信息
二. 代码实现
0. 研究示例代码
购买页下面有请求示例,但是并没有Nodejs的示例,这就让我头疼了很久(框起来的信息都非常重要,要看仔细了);
后来我在这个链接中【阿里云 OpenAPI 开发者门户】找到了Node.js版本的调用示例。
1. 发送短信验证码
async sendMessage(ctx, next) {
const customerPhoneNumber = ctx.request.query.customerPhoneNumber;
const AppCode = "<your-AppCode>"
const AppKey = "<your-AppKey>"
const AppSecret = "<your-AppSecret>"
const Host = "https://dfsns.market.alicloudapi.com" // 购买页示例中的主机地址
const Path = "/data/send_sms" // 购买页示例中的路径
const templateId = "TPL_0000" // 购买页示例中的模板编号
const client = new Core({
accessKeyId: AppKey,
accessKeySecret: AppSecret,
endpoint: Host + Path,
apiVersion: '2017-05-25'
});
// 生成n位随机0~9的验证码
const geneRandomCode = (n) => {
let code = ""
for (let i = 0; i < n; i++) {
code += parseInt(Math.random() * 10)
}
return code
}
const code = geneRandomCode(6)
const params = {
content: `code:${code}`,
["phone_number"]: customerPhoneNumber,
["template_id"]: templateId
}
const requestOption = {
method: 'POST',
formatParams: false,
headers: {
Authorization: `APPCODE ${AppCode}`,
["Content-Type"]: "application/x-www-form-urlencoded; charset=UTF-8"
}
}
try {
const response = await client.request('SendSms', params, requestOption)
// console.log('response', response)
if (response.status === 'OK') {
const message = {
customerPhoneNumber,
code,
expirationTime: new Date().getTime() + EXPIRATION_TIME + ""
}
console.log(message);
// 在数据库中保存用户手机号+短信验证码+过期时间戳
await userService.saveSMSCode(message)
ctx.body = {
code: '200',
message: '发送成功'
}
} else {
ctx.body = {
code: '400',
message: '发送失败'
}
}
} catch (error) {
console.log('error', error)
}
}
expirationTime过期时间我是通过getTime的方法获取当前的13位时间戳(单位毫秒ms),再加上一个常量
const EXPIRATION_TIME = 1000 * 60 * 5 // 5分钟
2. 在数据库中保存短信验证码等信息
讲道理在Redis中保存短信验证码设置过期时间会更加规范,简单起见我就保存在MySQL里了。
async saveSMSCode(message) {
const {
customerPhoneNumber,
code,
expirationTime
} = message
let statement = `SELECT * FROM smscode WHERE customerPhoneNumber = ?`
let result = await connection.execute(statement, [customerPhoneNumber])
// 如果查不到就插入新的数据 查到了就更新替换短信验证码 + 过期时间
if (result[0].length === 0) {
statement = `INSERT INTO smscode (customerPhoneNumber, code, ExpirationTime) VALUES(?, ?, ?)`
result = await connection.execute(statement, [customerPhoneNumber, code, expirationTime])
} else {
statement = `UPDATE smscode SET code = ? , ExpirationTime = ? WHERE customerPhoneNumber = ?`
result = await connection.execute(statement, [code, expirationTime, customerPhoneNumber])
}
}
3. 校验输入的注册信息
编写中间件verifyRegisterInfo对用户输入的注册信息进行校验;
主要从三个方面进行校验:
- 判断用户输入的信息是否合法,例如手机号长度等
- 在数据库中查询用户输入的手机号是否被注册过
- 查看验证码是否正确并未过期
const verifyRegisterInfo = async (ctx, next) => {
const {
customerPhoneNumber,
code
} = ctx.request.body
try {
// 1. 判断用户填入信息是否合法
// 前端已经判断
// 2. 查询手机号是否被注册过
let result = await userService.getPhone(customerPhoneNumber)
// console.log("查询手机号是否被注册过:", result);
if (result.length !== 0) {
const error = new Error(errorType.PHONE_ALREADY_EXISTS)
return ctx.app.emit("error", error, ctx)
}
// 3. 查看验证码是否正确或过期
const message = {
customerPhoneNumber,
code,
curTime: new Date().getTime()
}
result = await userService.checkCode(message)
// console.log("查看验证码是否正确或过期:", result);
if (result.length === 0) {
const error = new Error(errorType.CODE_IS_EXPIRED_OR_INCORRECT)
return ctx.app.emit("error", error, ctx)
}
await next()
} catch (err) {
console.log(err);
}
}
若用户传入的注册信息不合法,则会抛出错误,若没有错误则会执行next(),交给下一个中间件register处理
async getPhone(customerPhoneNumber) {
let statement = `SELECT * FROM customer WHERE customer_phone_number = ?`
let result = await connection.execute(statement, [customerPhoneNumber])
return result[0]
}
async checkCode(message) {
const {
customerPhoneNumber,
code,
curTime
} = message
const statement = `SELECT * FROM smscode WHERE customerPhoneNumber = ? AND code = ? AND expirationTime >= ?`
const result = await connection.execute(statement, [customerPhoneNumber, code, curTime])
return result[0]
}
4. 注册
编写中间件register;
在数据库中插入用户注册的数据;
async register(ctx, next) {
const {
customerPhoneNumber,
customerPassword
} = ctx.request.body
const message = {
customerPhoneNumber,
customerPassword
}
await userService.register(message)
ctx.body = {
status: 200,
message: "注册成功~"
}
}
async register(message) {
const {
customerPhoneNumber,
customerPassword
} = message
let statement = `INSERT INTO customer (customer_phone_number, customer_password) VALUES(?, ?)`
const result = await connection.execute(statement, [customerPhoneNumber, customerPassword])
return result[0]
}
以上步骤便完整了通过Koa调用阿里云短信服务实现用户注册的功能。