阿里云文件存储
-
采用服务端签名后直传的方式,不将图片传给服务端,而是从服务端获取签名,绕过服务端直接上传到OSS阿里云服务器,具体实例操作如下:
-
第一步,引入阿里云OSS上传坐标,配置上传所需的accesskey等信息:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
spring:
application:
name: mall-product
alicloud:
# 这里填写获取的access-key,secret-key信息,这一步建议新增一个Access用户专门管理文件上传模块。
access-key: xxx
secret-key: xxx
oss:
# 这里填写你选择的OSS服务地址,即所选在哪个区
endpoint: oss-cn-guangzhou.aliyuncs.com
bucket: xxx
- 第二步,编写获取签名的接口,供前端访问该接口并获取签名。
@RestController
public class OssController {
@Autowired
OSS ossClient;
@Value("${spring.cloud.alicloud.oss.endpoint}")
private String endPoint;
@Value("${spring.cloud.alicloud.oss.bucket}")
private String bucket;
@Value("${spring.cloud.alicloud.access-key}")
private String accessId;
@Value("${spring.cloud.alicloud.secret-key}")
private String accessKeySecret;
@RequestMapping("/oss/policy")
public R policy() {
//https://gulimall-clouds.oss-cn-beijing.aliyuncs.com/iqiyi.png
// Endpoint以杭州为例,其它Region请按实际情况填写。
String host = "https://" + bucket + "." + endPoint; // host的格式为 bucketname.endpoint
// callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
//String callbackUrl = "http://88.88.88.88:8888";
String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String dir = format + "/"; // 用户上传文件时指定的前缀。
// 创建OSSClient实例。
//OSS ossClient = new OSSClientBuilder().build(endPoint, accessId, accessKeySecret);
Map<String, String> respMap = null;
try {
long expireTime = 30;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
respMap = new LinkedHashMap<String, String>();
respMap.put("accessid", accessId);
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
} catch (Exception e) {
// Assert.fail(e.getMessage());
System.out.println(e.getMessage());
} finally {
ossClient.shutdown();
}
return R.ok().put("data",respMap);
}
}
-
第三步:如果是单体应用,则无需配置网关分发。如果配置了,需要在网关配置一下网关信息。这里图像上传是没有经过Nginx的,直接访问网关,由网关进行分发的。
-
第四步:elementUI有个上传组件upload,提供上传前的方法,我们可以在上传图片之前请求服务器获取签名凭证,如下所示:
beforeUpload(file) {
let _self = this;
return new Promise((resolve, reject) => {
policy()
.then(response => {
_self.dataObj.policy = response.data.policy;
_self.dataObj.signature = response.data.signature;
_self.dataObj.ossaccessKeyId = response.data.accessid;
_self.dataObj.key = response.data.dir +getUUID()+"_${filename}";
_self.dataObj.dir = response.data.dir;
_self.dataObj.host = response.data.host;
resolve(true);
})
.catch(err => {
console.log("出错了...",err)
reject(false);
});
});
},
- 第五步,在阿里云服务器开启允许跨域请求。
腾讯云短信发送
- 第一步,毫无疑问肯定是开通短信服务,申请签名以及模板啦。不知道是不是审核的就是慢还是周六日的原因,说是两个小时审核,我等了5,6个小时,最后是咨询客服去审核的。
- 第二步,审核通过后,就可以开始使用腾讯云的短信接口了,参考其Java SDK文档进行配置即可。
#导入maven坐标
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.270</version><!-- 注:这里只是示例版本号(可直接使用),可获取并替换为 最新的版本号,注意不要使用4.0.x版本(非最新版本) -->
#发送短信的核心代码
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
//导入可选配置类
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
// 导入对应SMS模块的client
import com.tencentcloudapi.sms.v20210111.SmsClient;
// 导入要请求接口对应的request response类
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
/**
* Tencent Cloud Sms Sendsms
* */
public class SendSms
{
public static void main(String[] args)
{
try {
/* 必要步骤:
* 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。
* 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。
* 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人,
* 以免泄露密钥对危及你的财产安全。
* CAM密匙查询: https://console.cloud.tencent.com/cam/capi*/
Credential cred = new Credential("secretId", "secretKey");
// 实例化一个http选项,可选,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
// 设置代理
// httpProfile.setProxyHost("真实代理ip");
// httpProfile.setProxyPort(真实代理端口);
/* SDK默认使用POST方法。
* 如果你一定要使用GET方法,可以在这里设置。GET方法无法处理一些较大的请求 */
httpProfile.setReqMethod("POST");
/* SDK有默认的超时时间,非必要请不要进行调整
* 如有需要请在代码中查阅以获取最新的默认值 */
httpProfile.setConnTimeout(60);
/* SDK会自动指定域名。通常是不需要特地指定域名的,但是如果你访问的是金融区的服务
* 则必须手动指定域名,例如sms的上海金融区域名: sms.ap-shanghai-fsi.tencentcloudapi.com */
httpProfile.setEndpoint("sms.tencentcloudapi.com");
/* 非必要步骤:
* 实例化一个客户端配置对象,可以指定超时时间等配置 */
ClientProfile clientProfile = new ClientProfile();
/* SDK默认用TC3-HMAC-SHA256进行签名
* 非必要请不要修改这个字段 */
clientProfile.setSignMethod("HmacSHA256");
clientProfile.setHttpProfile(httpProfile);
/* 实例化要请求产品(以sms为例)的client对象
* 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,或者引用预设的常量 */
SmsClient client = new SmsClient(cred, "ap-guangzhou",clientProfile);
/* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
* 你可以直接查询SDK源码确定接口有哪些属性可以设置
* 属性可能是基本类型,也可能引用了另一个数据结构
* 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */
SendSmsRequest req = new SendSmsRequest();
/* 填充请求参数,这里request对象的成员变量即对应接口的入参
* 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义
* 基本类型的设置:
* 帮助链接:
* 短信控制台: https://console.cloud.tencent.com/smsv2
* sms helper: https://cloud.tencent.com/document/product/382/3773 */
/* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */
String sdkAppId = "1400009099";
req.setSmsSdkAppId(sdkAppId);
/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名,签名信息可登录 [短信控制台] 查看 */
String signName = "签名内容";
req.setSignName(signName);
/* 国际/港澳台短信 SenderId: 国内短信填空,默认未开通,如需开通请联系 [sms helper] */
String senderid = "";
req.setSenderId(senderid);
/* 用户的 session 内容: 可以携带用户侧 ID 等上下文信息,server 会原样返回 */
String sessionContext = "xxx";
req.setSessionContext(sessionContext);
/* 短信号码扩展号: 默认未开通,如需开通请联系 [sms helper] */
String extendCode = "";
req.setExtendCode(extendCode);
/* 模板 ID: 必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看 */
String templateId = "400000";
req.setTemplateId(templateId);
/* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
* 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 */
String[] phoneNumberSet = {"+8621212313123", "+8612345678902", "+8612345678903"};
req.setPhoneNumberSet(phoneNumberSet);
/* 模板参数: 若无模板参数,则设置为空 */
String[] templateParamSet = {"5678"};
req.setTemplateParamSet(templateParamSet);
/* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
* 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */
SendSmsResponse res = client.SendSms(req);
// 输出json格式的字符串回包
System.out.println(SendSmsResponse.toJsonString(res));
// 也可以取出单个值,你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
System.out.println(res.getRequestId());
} catch (TencentCloudSDKException e) {
e.printStackTrace();
}
}
}
</dependency>
- 第三步,为了方便使用,我们可以将该接口抽取为一个组件,其具体的参数我们可以通过放在yml文件统一配置更改。只需要通过
ConfigurationProperties(prefix="")
指定yml中相关配置所在的位置,通过@Data,@Component即可自动注入,如下所示。不过值得注意的是,腾讯云发送短信的方法setPhoneNumberSet
默认参数是个数组,封装成接口的话得注意一下这里,不要只穿字符串类型,得把它变成数组类型。
@ConfigurationProperties(prefix = "spring.cloud.txcloud")
@Component
@Data
public class SmsComponent {
private String secretId;
private String secretKey;
private String reqMethod;
private String endPoint;
private String sdkAppId;
private String signName;
private String templateId;
private static final String signMethod = "HmacSHA256";
public void SendMessage(String phone,String code){
try {
Credential cred = new Credential(secretId, secretKey);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setReqMethod(reqMethod);
httpProfile.setConnTimeout(60);
httpProfile.setEndpoint(endPoint);
ClientProfile clientProfile = new ClientProfile();
clientProfile.setSignMethod(signMethod);
clientProfile.setHttpProfile(httpProfile);
SmsClient client = new SmsClient(cred, "ap-guangzhou",clientProfile);
SendSmsRequest req = new SendSmsRequest();
req.setSmsSdkAppId(sdkAppId);
req.setSignName(signName);
req.setTemplateId(templateId);
String[] numberSet = new String[1];
numberSet[0] = "86"+phone;
req.setPhoneNumberSet(numberSet);
/* 模板参数: 验证码,可通过UUID生成 */
//String randomCode = this.getRandomCode();
String[] codeSet = new String[1];
codeSet[0] = code.split("_")[0];
String[] templateParamSet = codeSet;
req.setTemplateParamSet(templateParamSet);
/* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
* 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */
SendSmsResponse res = client.SendSms(req);
// 输出json格式的字符串回包
System.out.println(SendSmsResponse.toJsonString(res));
// 也可以取出单个值,你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
System.out.println(res.getRequestId());
} catch (TencentCloudSDKException e) {
e.printStackTrace();
}
}
}
- 第四步,调用接口就可以啦。
后端层面的接口防刷:防止用户多次刷新页面请求接口,造成短信的浪费。可以通过腾讯云的控制台开启发送频率限制,但是这个功能好像只能给企业用户使用,个人用户使用不了。我们就可以通过在redis中,设置用户的手机号码为key,value为短信验证码+时间戳的形式,在发送请求的时候检验redis中该用户的时间戳和当前时间戳之差是否超过60秒,如果超过则允许发送,否则直接返回发送频率过快的信息。整体逻辑代码如下所示:
@ResponseBody
@GetMapping("/sms/sendCode")
public R sendCode(@RequestParam("phone") String phone){
//1、接口防刷,防止刷新页面之后无限刷
String s = redisTemplate.opsForValue().get(AuthConstant.SMS_CODE_CACHE_PREFIX + phone);
if(!StringUtils.isNullOrEmpty(s)){
long time = Long.parseLong(s.split("_")[1]);
if(System.currentTimeMillis()-time<60000){
//60秒内禁止再发
return R.error(BizCodeEnume.MESSAGE_SEND_EXCEPTION.getCode(),BizCodeEnume.MESSAGE_SEND_EXCEPTION.getMsg());
}
}
//2、验证码缓存校验.key-phone,value-code
RandomUtils randomUtils = new RandomUtils();
String randomCode = randomUtils.getRandomCode()+"_"+System.currentTimeMillis();
redisTemplate.opsForValue().set(AuthConstant.SMS_CODE_CACHE_PREFIX+phone,randomCode,10, TimeUnit.MINUTES);
//微服务远程调用第三方服务的短信发送接口
thirdPartyFeignService.sendCode(phone,randomCode);
return R.ok();
}