Springboot+Vue实现短信与邮箱验证码登录
体验网址:http://mxyit.com
示例
1、新增依赖
<!-- 短信服务 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.1.0</version>
</dependency>
<!-- 邮件服务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2、前端示例
Login.vue
<el-tabs v-show="!isRegist" type="border-card" class="login-tab">
<el-tab-pane><span slot="label"><i class="el-icon-mobile-phone"></i> 手机号登录</span>
<el-form
ref="phoneForm"
:model="phoneForm"
:rules="loginRules"
autocomplete="off"
:hide-required-asterisk="true"
size="medium"
>
<el-form-item prop="phoneNo">
<span class="svg-container">
<svg-icon icon-class="user"/>
</span>
<el-input
v-model="phoneForm.phoneNo"
placeholder="请输入手机号/自动注册"
/>
</el-form-item>
<div>
<div style="width: 50%;display: inline-block;">
<el-form-item prop="code">
<span class="svg-container">
<svg-icon icon-class="password"/>
</span>
<el-input
v-model="phoneForm.code"
maxlength="6"
placeholder="请输入验证码"
/>
</el-form-item>
</div>
<div style="display: inline-block;margin-left: 20px;">
<el-button
:loading="codeLoading"
:disabled="isDisable"
size="small"
round
@click="sendMsg(1)"
>短信验证
</el-button>
<span class="status">{{ statusMsg }}</span>
</div>
</div>
<el-button
:loading="loading"
type="primary"
style="width:100%;margin-bottom:15px;"
@click.native.prevent="phoneLogin"
>登录/注册
</el-button>
<p class="tips">
<a @click.prevent="isRegist=true" type="primary">账号密码登录</a>
</p>
</el-form>
</el-tab-pane>
<el-tab-pane><span slot="label"><i class="el-icon-mobile-phone"></i> 邮箱登录</span>
<el-form
ref="emailForm"
:model="emailForm"
:rules="loginRules"
autocomplete="off"
:hide-required-asterisk="true"
size="medium"
>
<el-form-item prop="email">
<span class="svg-container">
<svg-icon icon-class="user"/>
</span>
<el-input
v-model="emailForm.email"
placeholder="请输入邮箱/自动注册"
/>
</el-form-item>
<div>
<div style="width: 50%;display: inline-block;">
<el-form-item prop="code">
<span class="svg-container">
<svg-icon icon-class="password"/>
</span>
<el-input
v-model="emailForm.code"
maxlength="6"
placeholder="请输入验证码"
/>
</el-form-item>
</div>
<div style="display: inline-block;margin-left: 20px;">
<el-button
:loading="codeLoading"
:disabled="isDisable"
size="small"
round
@click="sendMsg(2)"
>邮箱验证
</el-button>
<span class="status">{{ statusMsg }}</span>
</div>
</div>
<el-button
:loading="loading"
type="primary"
style="width:100%;margin-bottom:15px;"
@click.native.prevent="emailLogin"
>登录/注册
</el-button>
<p class="tips">
<a @click.prevent="isRegist=true" type="primary">账号密码登录</a>
</p>
</el-form>
</el-tab-pane>
<div style="height: 70px;">
<el-divider><span style="color: #8c92a4">其它方式登录</span></el-divider>
<social-sign/>
</div>
</el-tabs>
Login.vue
<script>
import axios from 'axios'
export default {
name: 'Login',
data() {
const validateUsername = (rule, value, callback) => {
if (value.length === 0) {
callback(new Error('请输入正确的用户名'))
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (value.length < 6) {
callback(new Error('密码不能小于6位'))
} else {
callback()
}
}
const validatePhone = (rule, value, callback) => {
const reg = /^[1][3-9][0-9]{9}$/;
if (value == '' || value == undefined || value == null) {
callback(new Error('请输入手机号码'));
} else {
if ((!reg.test(value)) && value != '') {
callback(new Error('请输入正确的手机号码'));
} else {
callback();
}
}
}
return {
loginForm: {
username: '',
password: '',
key: '',
captcha: ''
},
phoneForm: {
phoneNo: '',
code: ''
},
emailForm: {
email: '',
code: ''
},
loginRules: {
username: [{required: true, trigger: 'blur', validator: validateUsername}],
password: [{required: true, trigger: 'blur', validator: validatePassword}],
phoneNo: [{required: true, trigger: 'blur', validator: validatePhone}],
email: [{
required: true,
type: 'email',
message: '请输入邮箱',
trigger: 'blur'
}],
captcha: [{
required: true,
type: 'string',
message: '请输入验证码',
trigger: 'blur'
}],
code: [{
required: true,
type: 'string',
message: '请输入验证码',
trigger: 'blur'
}],
pwd: [{
required: true,
message: '创建密码',
trigger: 'blur'
}, {pattern: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,20}$/, message: '密码必须同时包含数字与字母,且长度为 8-20位'}],
cpwd: [{
required: true,
message: '确认密码',
trigger: 'blur'
}, {
validator: (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'))
} else if (value !== this.ruleForm.pwd) {
callback(new Error('两次输入密码不一致'))
} else {
callback()
}
},
trigger: 'blur'
}]
},
statusMsg: '',
loading: false,
isDisable: false,// 验证码按钮禁用
codeLoading: false,// 验证码loading
passwordType: 'password',
redirect: undefined,
isRegist: true,
imageSrc: '',
verKey: '',
showDialog: false
}
},
watch: {
$route: {
handler: function (route) {
this.redirect = route.query && route.query.redirect
},
immediate: true
}
},
created() {
this.getCaptcha();
},
methods: {
/*获取验证码*/
getCaptcha() {
axios({
url: window.SITE_CONFIG['systemUrl'] + '/api/foreign/sms/get/captcha',
method: 'get',
params: {}
}).then(res => {
console.log(res)
let result = res.data.data
this.loginForm.key = result.key;
this.imageSrc = result.image;
}).catch(err => {
console.log(err)
})
},
sendMsg: function (type) {
if (type === 1) {
const self = this
let phonePass
let timeRid
if (timeRid) {
return false
}
self.statusMsg = ''
this.$refs['phoneForm'].validateField('phoneNo', (valid) => {
phonePass = valid
})
// 向后台API验证码发送
if (!phonePass) {
self.codeLoading = true
self.statusMsg = '验证码发送中...'
let url = "";
let params = {};
axios({
url: window.SITE_CONFIG['systemUrl'] + '/api/foreign/sms/customer/sendMessageCode',
method: 'get',
params: {phoneNo: self.phoneForm.phoneNo}
}).then(res => {
console.log(res)
let result = res.data
if (result.code == 200) {
this.$message({
showClose: true,
message: '发送成功,验证码有效期5分钟',
type: 'success'
})
let count = 60
self.phoneForm.code = ''
self.codeLoading = false
self.isDisable = true
self.statusMsg = `验证码已发送,${count--}秒后重新发送`
timeRid = window.setInterval(function () {
self.statusMsg = `验证码已发送,${count--}秒后重新发送`
if (count <= 0) {
window.clearInterval(timeRid)
self.isDisable = false
self.statusMsg = ''
}
}, 1000)
} else {
this.$message({
showClose: true,
message: result.data,
type: 'warning'
})
this.isDisable = false
this.statusMsg = ''
this.codeLoading = false
}
}).catch(err => {
console.log(err)
this.isDisable = false
this.statusMsg = ''
this.codeLoading = false
console.log(err.data)
})
}
}
if (type === 2) {
const self = this
let emailPass
let timeRid
if (timeRid) {
return false
}
self.statusMsg = ''
this.$refs['emailForm'].validateField('email', (valid) => {
emailPass = valid
})
// 向后台API验证码发送
if (!emailPass) {
self.codeLoading = true
self.statusMsg = '验证码发送中...'
axios({
url: window.SITE_CONFIG['systemUrl'] + '/api/foreign/sms/customer/sendEmailMessage',
method: 'get',
params: {email: self.emailForm.email}
}).then(res => {
console.log(res)
let result = res.data
if (result.code == 200) {
this.$message({
showClose: true,
message: '发送成功,验证码有效期5分钟',
type: 'success'
})
let count = 60
self.emailForm.code = ''
self.codeLoading = false
self.isDisable = true
self.statusMsg = `验证码已发送,${count--}秒后重新发送`
timeRid = window.setInterval(function () {
self.statusMsg = `验证码已发送,${count--}秒后重新发送`
if (count <= 0) {
window.clearInterval(timeRid)
self.isDisable = false
self.statusMsg = ''
}
}, 1000)
} else {
this.$message({
showClose: true,
message: result.data,
type: 'warning'
})
this.isDisable = false
this.statusMsg = ''
this.codeLoading = false
}
}).catch(err => {
console.log(err)
this.isDisable = false
this.statusMsg = ''
this.codeLoading = false
console.log(err.data)
})
}
}
},
showPwd() {
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus()
})
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.show = false
this.$store.dispatch('user/login', this.loginForm).then(() => {
this.$router.push({path: '/'})
this.loading = false
this.show = true
}).catch(() => {
this.loading = false
this.show = true
this.getCaptcha()
})
} else {
console.log('操作异常')
this.getCaptcha()
return false
}
})
},
phoneLogin() {
this.$refs.phoneForm.validate(valid => {
if (valid) {
this.loading = true
this.show = false
this.$store.dispatch('user/phoneLogin', this.phoneForm).then(() => {
this.$router.push({path: '/'})
this.loading = false
this.show = true
}).catch(() => {
this.loading = false
this.show = true
})
} else {
console.log('操作异常')
return false
}
})
},
emailLogin() {
this.$refs.emailForm.validate(valid => {
if (valid) {
this.loading = true
this.show = false
this.$store.dispatch('user/emailLogin', this.emailForm).then(() => {
this.$router.push({path: '/'})
this.loading = false
this.show = true
}).catch(() => {
this.loading = false
this.show = true
})
} else {
console.log('操作异常')
return false
}
})
}
}
}
</script>
3、后端示例
SmsController
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 短信验证码
*
* @author my
*/
@RestController
@CrossOrigin
@RequestMapping("/api/foreign/sms")
public class SmsController {
@Resource
private SmsService smsService;
/**
* 发送短信验证码
*
* @return
*/
@ApiOperation(value = "发送短信验证码", httpMethod = "GET", notes = "发送短信验证码")
@GetMapping(value = "/customer/sendMessageCode")
public String sendSmsMessage(@RequestParam(value = "phoneNo") String phoneNo) {
return smsService.sendSmsMessage(phoneNo);
}
/**
* 发送邮箱验证码
*
* @return
*/
@ApiOperation(value = "发送邮箱验证码", httpMethod = "GET", notes = "发送邮箱验证码")
@GetMapping(value = "/customer/sendEmailMessage")
public String sendEmailMessage(@RequestParam(value = "email") String email) {
return smsService.sendEmailMessage(email);
}
/**
* 获取验证码
*
* @return
*/
@ApiOperation(value = "获取验证码", httpMethod = "GET", notes = "获取验证码")
@GetMapping(value = "/get/captcha")
public String captcha() {
return smsService.captcha();
}
}
SmsService
import com.mxy.common.core.utils.DateUtils;
import com.mxy.common.core.utils.RedisUtil;
import com.mxy.common.core.utils.ServiceResult;
import com.wf.captcha.SpecCaptcha;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
@Service
public class SmsService {
@Value("${sms.max.limit}")
private Integer maxLimit;
@Value("${sms.sendError.content}")
private String sendErrorContent;
@Resource
private RedisUtil redisUtil;
@Resource
private SmsSend smsSend;
private static final String MESSAGE_LIMIT = "messager_limit:";
private static final String SEND_LIMIT = "sendmsg_limit_count:";
private static final String PHONE_NO = "message_phone_no:";
private static final String EMAIL_MESSAGE_LIMIT = "email_messager_limit:";
private static final String EMAIL_SEND_LIMIT = "email_sendmsg_limit_count:";
private static final String EMAIL_NO = "message_email_no:";
private static final String CAPTCHA_NO = "message_captcha_no:";
private Random random = new Random();
public String sendSmsMessage(String phoneNo) {
// 非空检验
if (StringUtils.isEmpty(phoneNo)) {
return ServiceResult.error("电话号码不能为空哦");
}
String sendMsgKey = SEND_LIMIT + phoneNo;
if (redisUtil.hasKey(sendMsgKey) && (int) redisUtil.get(sendMsgKey) > maxLimit) {
return ServiceResult.error(sendErrorContent);
}
String phoneNoKey = PHONE_NO + phoneNo;
// 如果验证码已发送 作废之前的验证码
if (redisUtil.hasKey(phoneNoKey)) {
redisUtil.del(phoneNoKey);
}
String limitKey = MESSAGE_LIMIT + phoneNo;
if (redisUtil.hasKey(limitKey)) {
return ServiceResult.error("一分钟只能请求一次哦");
}
// 随机生成6位验证码
String verifyCode = String.valueOf(random.nextInt(899999) + 100000);
// 限制60秒只能生成一次验证码
redisUtil.set(limitKey, verifyCode, 60);
// 短信验证码 5分钟失效
redisUtil.set(phoneNoKey, verifyCode, 300);
int count = (int) (redisUtil.get(sendMsgKey) == null ? 0 : redisUtil.get(sendMsgKey));
// 计算当天23点59分59秒的秒数
long expireTime = DateUtils.getDifferentTimes();
// 当日0点过期
redisUtil.set(sendMsgKey, count + 1, expireTime);
// 异步请求下发短信
smsSend.sendSmsMessage(phoneNo, verifyCode, 2);
return ServiceResult.success("短信发送成功");
}
public String sendEmailMessage(String email) {
// 非空检验
if (StringUtils.isEmpty(email)) {
return ServiceResult.error("邮箱地址不能为空哦");
}
String sendMsgKey = EMAIL_SEND_LIMIT + email;
if (redisUtil.hasKey(sendMsgKey) && (int) redisUtil.get(sendMsgKey) > maxLimit) {
return ServiceResult.error(sendErrorContent);
}
String emailNoKey = EMAIL_NO + email;
// 如果验证码已发送 作废之前的验证码
if (redisUtil.hasKey(emailNoKey)) {
redisUtil.del(emailNoKey);
}
String limitKey = EMAIL_MESSAGE_LIMIT + email;
if (redisUtil.hasKey(limitKey)) {
return ServiceResult.error("一分钟只能请求一次哦");
}
// 随机生成6位验证码
String verifyCode = String.valueOf(random.nextInt(899999) + 100000);
// 限制60秒只能生成一次验证码
redisUtil.set(limitKey, verifyCode, 60);
// 短信验证码 5分钟失效
redisUtil.set(emailNoKey, verifyCode, 300);
int count = (int) (redisUtil.get(sendMsgKey) == null ? 0 : redisUtil.get(sendMsgKey));
// 计算当天23点59分59秒的秒数
long expireTime = DateUtils.getDifferentTimes();
// 当日0点过期
redisUtil.set(sendMsgKey, count + 1, expireTime);
// 异步请求下发邮件
smsSend.sendEmailMessage(email, verifyCode);
return ServiceResult.success("邮件发送成功");
}
public String captcha() {
SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);
String verCode = specCaptcha.text().toLowerCase();
String key = UUID.randomUUID().toString();
// 存入redis并设置过期时间为300秒
redisUtil.set(CAPTCHA_NO + key, verCode, 300);
// 将key和base64返回给前端
Map<String, Object> map = new HashMap<>();
map.put("key", key);
map.put("image", specCaptcha.toBase64());
return ServiceResult.success(map);
}
}
SmsSend
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import com.mxy.common.core.entity.SysSmsSendLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 短信发送
*
* @author my
*/
@Slf4j
@Component
public class SmsSend {
@Value("${sms.accessKeyId}")
private String accessKeyId;
@Value("${sms.secret}")
private String secret;
@Resource
private JavaMailSender sender;
/**
* @description: 短信验证码发送
* @param: phoneNo-接收号码、verifyCode-验证码、type-短信模板类型(默认通用模板)(1-注册、2-登录、0-通用)
* 签名说明:
* 个人日常博客
* 模板说明:
* 通用模板:SMS_25073XXXX 您的验证码${code},该验证码5分钟内有效,请勿泄漏于他人!
* 登录验证码模板:SMS_25073XXXX 验证码为:${code},您正在登录,若非本人操作,请勿泄露。
* 注册验证码模板:SMS_25075XXXX 您正在申请手机注册,验证码为:${code},5分钟内有效!
**/
@Async("threadPoolTaskExecutor")
public void sendSmsMessage(String phoneNo, String verifyCode, int type) {
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou",
accessKeyId, secret);
IAcsClient client = new DefaultAcsClient(profile);
// 组装请求对象
CommonRequest request = new CommonRequest();
// 短信API产品域名(接口地址固定,无需修改)
request.setDomain("dysmsapi.aliyuncs.com");
request.setVersion("2017-05-25");
request.setAction("SendSms");
request.putQueryParameter("RegionId", "cn-hangzhou");
// 接收号码
request.putQueryParameter("PhoneNumbers", phoneNo);
// 短信签名
request.putQueryParameter("SignName", "个人日常博客");
// 短信模板
String smsTemplate = "";
switch (type) {
case 1:
request.putQueryParameter("TemplateCode", "SMS_25075XXXX");
smsTemplate = "【个人日常博客】您正在申请手机注册,验证码为:" + verifyCode + ",5分钟内有效!";
break;
case 2:
request.putQueryParameter("TemplateCode", "SMS_25073XXXX");
smsTemplate = "【个人日常博客】验证码为:" + verifyCode + ",您正在登录,若非本人操作,请勿泄露。";
break;
default:
request.putQueryParameter("TemplateCode", "SMS_25073XXXX");
smsTemplate = "【个人日常博客】您的验证码" + verifyCode + ",该验证码5分钟内有效,请勿泄漏于他人!";
break;
}
// 验证码
request.putQueryParameter("TemplateParam", "{code:" + verifyCode + "}");
try {
CommonResponse response = client.getCommonResponse(request);
// 记录发送记录,可忽略
SysSmsSendLog sendLog = new SysSmsSendLog();
sendLog.setPhone(phoneNo);
sendLog.setRequest(smsTemplate);
sendLog.setResponse(String.valueOf(response.getData()));
sendLog.insert();
log.info(phoneNo + "短信发送:" + response.getData());
} catch (Exception e) {
e.printStackTrace();
log.info(phoneNo + "短信发送失败:" + e.getMessage());
}
}
@Async("threadPoolTaskExecutor")
public void sendEmailMessage(String email, String verifyCode) {
try {
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom("you.email@qq.com");
mailMessage.setTo(email);
mailMessage.setSubject("个人日常博客验证码");
mailMessage.setText("您的验证码" + verifyCode + ",该验证码5分钟内有效,请勿泄漏于他人!");
sender.send(mailMessage);
SysSmsSendLog sendLog = new SysSmsSendLog();
sendLog.setPhone(email);
sendLog.setRequest(mailMessage.getText());
sendLog.setResponse("OK");
log.info(email + "->邮件发送:" + mailMessage.getText());
sendLog.insert();
} catch (MailException e) {
e.printStackTrace();
log.info(email + "邮件发送失败:" + e.getMessage());
}
}
}
工具类
DateUtils
/**
* 计算当天23点59分59秒的秒数
*/
public static Long getDifferentTimes() {
//计算当前时间到0点的秒数
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_YEAR, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
}
RedisUtil
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Redis工具类
*
* @author my
* @date 2022-02-10
*/
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
}
配置文件
application.yml
# 配置-email
spring:
mail:
host: smtp.qq.com # 发送邮件的服务器地址
username: you.email@qq.com # 开启 IMAP/SMTP服务 的qq邮箱的账号
password: xxxxxxxxxxxx # 开启 IMAP/SMTP服务 获得的授权码
default-encoding: UTF-8
# 阿里云短信秘钥
sms:
accessKeyId: xxxxxxxxxxxx
secret: xxxxxxxxxxxx
max:
limit: 10
sendError:
content: 当日的获取验证码次数已用尽,请联系管理员解除限制。
Springboot+Vue实现短信与邮箱验证码登录的更多相关文章
- java springboot activemq 邮件短信微服务,解决国际化服务的国内外兼容性问题,含各服务商调研情况
java springboot activemq 邮件短信微服务,解决国际化服务的国内外兼容性问题,含各服务商调研情况 邮件短信微服务 spring boot 微服务 接收json格式参数 验证参数合 ...
- day7(vue发送短信)
1.vue发送短信逻辑 前端函数如下,js方法代码无需更改,前端代码逻辑在components\common\lab_header.vue 只需要修改components\axios_api\http ...
- Android之发送短信和接收验证码
最近项目需求需要发送短信和接收验证码并将验证码显示在输入框中 以下是我的记录 前提---权限 <uses-permission android:name="andro ...
- SpringBoot整合阿里短信服务
导读 由于最近手头上需要做个Message Gateway,涉及到:邮件(点我直达).短信.公众号(点我直达)等推送功能,网上学习下,整理下来以备以后使用. 步骤 点我直达 登录短信服务控制台 点我直 ...
- Win10《芒果TV》更新v3.8.50勇敢版:新增短信和扫码登录
勇敢,是心中最初的信仰,实景科幻实验节目<勇敢的世界>,重装上阵对抗升级,<中餐厅2>皇阿玛圣驾亲临,坐镇中国味道.Win10版<芒果TV>全平台同步更新勇敢版v3 ...
- vue实现短信验证码登录
无论是移动端还是pc端登录或者注册界面都会见到手机验证码登录这个功能,输入手机号,得到验证码,最后先服务器发送请求,保存登录的信息,一个必不可少的功能 思路 1,先判断手机号和验证是否为空, 2,点击 ...
- springboot 使用阿里云短信服务发送验证码
一.申请阿里云短信服务 1.申请签名 2.申请模板 3.创建accesskey(鼠标悬停在右上角头像) 二.代码实现 1.springboot引入maven依赖 <dependency> ...
- iOS使用技能 - 短信,语言验证码的获取与验证小结
最近有学习一个小技能,这里小结一下,分享给大家,互相交流. 首先是大体步骤: 在mob官网注册,然后添加短信验证的应用 使用cocoapods导入框架 Podfile文件: platform :ios ...
- Jave Web阿里云短信服务发送验证码
首先得在阿里云根据流程开通短信服务,申请签名和模版,具体看文档 因为这是个web项目,用到了thymeleaf模板,所以在pom.xml中加入如下依赖 <dependency> <g ...
- java实现阿里云短信服务发送验证码
由于做项目的时候遇到了接第三方短信服务,所以记录一下. 一.新建一个maven项目并导入相关依赖 <!--手机发送短信验证码--> <dependency> <group ...
随机推荐
- 函数传参 Java JavaScript python 都是按值传递的
实验代码如下: Java python JavaScript:
- 艰难的 debug 经历,vscode 无法获取远程环境 ssh 报错,windows 11 ssh
背景介绍 要做系统结构实验,学校和华为云合作使用华为云的 aarch64 裸机,需要使用 ssh 远程开发,笔者为了追求良好的开发体验,决定使用 vscode 开发,实验环境配置过程中遇到了两个问题, ...
- 【unity萌新第一步】Unity的Hello World(适合小白)
Unity萌新的第一步:使用unity写第一个Hello World IT界有个笑话:"我擅长用各种语言写Hello World".我讲这个笑话的目的是,指出:写一个HelloWo ...
- 非线性优化-NLopt
通过 对 一个 数学 模型 的求解 来介绍 NLopt的使用方法 数学模型: 这个是目标函数 求满足 条件的情况下 x2的开平方最小 边界约束 非线性不等式约束如下 有两个参数 x1 和 x2 ,其中 ...
- label勾选问题,checkbox
<input id="overck_21" data-role="none" name="check" class="reg ...
- 02#Vue3 Transition 过渡:切换路由组件
复习作用域插槽 组件可以被插入些许节点作为其子节点,插槽<slot>就是一个接口(或桥梁)引导这些节点进入组件.这些节点应该被渲染到组件里的具体哪个位置,就是具名插槽的作用. 就像是给手机 ...
- 【深入浅出 Yarn 架构与实现】4-5 RM 行为探究 - 启动 ApplicationMaster
本节开始,将对 ResourceManager 中一些常见行为进行分析探究,看某些具体关键的行为,在 RM 中是如何流转的.本节将深入源码探究「启动 ApplicationMaster」的具体流程. ...
- nginx中多ip多域名多端口配置
1.Nginx中多IP配置: server { listen 80; server_name 192.168.15.7; location / { root /opt/Super_Marie; ind ...
- Spark 要点总结及优化
Spark Components: 角色组成: Driver : 由SparkContext创建,运行在main方法,负责资源申请与调度,程序分发,接收每个分区的计算结果 Cluster mana ...
- css 属性选择器需要加引号吗
平常我们是不加引号的: HTML: <div data-a='aq1'>99</div> CSS: [data-a=aq1]{ color: #f00; } 想加上也行 ...