项目就更新到第六章了,剩下的内容 放百度网盘里面了,需要的来取。
链接:https://pan.baidu.com/s/19LHPw36dsxPB75z_FHS64Q?pwd=8h89
提取码:8h89
简介: RestTemplate里面的存在的问题你知道多少
还原代码(暂时不用异步)
同步发送+resttemplate未池化
问题分析
重新认识RestTemplate
问题解决
简介: 高性能RestTemplate封装配置实战
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
/**
* @return
*/
@Bean
public HttpClient httpClient() {
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
//设置整个连接池最大连接数
connectionManager.setMaxTotal(500);
//MaxPerRoute路由是对maxTotal的细分,每个主机的并发,这里route指的是域名
connectionManager.setDefaultMaxPerRoute(200);
RequestConfig requestConfig = RequestConfig.custom()
//返回数据的超时时间
.setSocketTimeout(20000)
//连接上服务器的超时时间
.setConnectTimeout(10000)
//从连接池中获取连接的超时时间
.setConnectionRequestTimeout(1000)
.build();
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.build();
}
简介: 【10倍+提升】Jmeter5.x压测 优化后RestTemplate前后性能对比
简介:调用第三方短信验证码组件性能优化实战
调整代码
采用异步调用方式
采用 resttemplate池化方式
短信邮箱轰炸机
手机短信轰炸机是批量、循环给手机无限发送各种网站的注册验证码短信的方法。
最早发明是用来整治街头广告电话号泛滥的一种手段,采用“手机短信轰炸机”软件可无限发送垃圾短信到牛皮癣小广告的手机号码上,使对方的手机快速消耗电量,变成高频率振动棒,且无法正常使用。
“短信轰炸机”可严厉打击城市“牛皮癣”,还城市明净容颜。
某次大型程序员相亲现场-老王得罪了小王, 小王不爽,就道听途说知道了”短信轰炸机“,1天50元,轰炸了5天还打折300元。
一天内接到来自全国各地数千个陌生电话短信的轰炸骚扰,导致个人通讯中断,被工作生活受到严重影响,连刚相亲到的女友没没法联系上了。
原理
很多人都用手机注册一些网站的验证了,比如手机验证码。先填手机号,然后发一条验证码过去,输入验证码,完成验证,注册成功。
* 寻找大量肉鸡网站,寻找发送验证码的请求接口
* 如果找不到接口,也可以使用自动化UI工具触发
* 编写程序和调度任务,相关脚本录入数据库
* 输入目标手机号或者邮箱,触发攻击
公司带来的损失
如何避免自己的网站成为”肉鸡“或者被刷呢
是否可以一劳永逸???
没有百分百的安全,验证码是可以破解的,ip也是可以租用代理ip的
攻防永远是有的,只过加大了攻击者的成本,ROI划不过来自然就放弃了
简介:谷歌开源kaptcha图形验证码开发
Kaptcha 框架介绍 谷歌开源的一个可高度配置的实用验证码生成工具
聚合工程依赖添加(使用国内baomidou二次封装的springboot整合starter)
<dependency>
<groupId>com.baomidougroupId>
<artifactId>kaptcha-spring-boot-starterartifactId>
<version>1.1.0version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>kaptcha-spring-boot-starterartifactId>
dependency>
@Configuration
public class CaptchaConfig {
/**
* 验证码配置
* Kaptcha配置类名
*
* @return
*/
@Bean
@Qualifier("captchaProducer")
public DefaultKaptcha kaptcha() {
DefaultKaptcha kaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// properties.setProperty(Constants.KAPTCHA_BORDER, "yes");
// properties.setProperty(Constants.KAPTCHA_BORDER_COLOR, "220,220,220");
// //properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "38,29,12");
// properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "147");
// properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "34");
// properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "25");
// //properties.setProperty(Constants.KAPTCHA_SESSION_KEY, "code");
//验证码个数
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
// properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Courier");
//字体间隔
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE,"8");
//干扰线颜色
// properties.setProperty(Constants.KAPTCHA_NOISE_COLOR, "white");
//干扰实现类
properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
//图片样式
properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.WaterRipple");
//文字来源
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789");
Config config = new Config(properties);
kaptcha.setConfig(config);
return kaptcha;
}
}
简介:池化思想应用-Redis6.X配置连接池实战
连接池好处
连接池配置 common项目
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
配置Redis连接
redis:
client-type: jedis
host: 120.79.150.146
password: class.net
port: 6379
jedis:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 100
# 连接池中的最大空闲连接
max-idle: 100
# 连接池中的最小空闲连接
min-idle: 100
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: 60000
序列化配置
@Configuration
public class RedisTemplateConfiguration {
/**
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置key和value的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// 设置hashKey和hashValue的序列化规则
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
简介:账号微服务开发图形验证码接口+Try-with-resource
redis做隔离, 多集群:核心集群和非核心集群,高并发集群和非高并发集群
验证码接口开发
/**
*临时使用10分钟有效,方便测试
*/
private static final long CAPTCHA_CODE_EXPIRED = 60 * 1000 * 10;
/**
* 获取图形验证码
* @param request
* @param response
*/
@GetMapping("captcha")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
String captchaText = captchaProducer.createText();
log.info("图形验证码:{}", captchaText);
//存储
redisTemplate.opsForValue().set(getCaptchaKey(request),
captchaText, CAPTCHA_CODE_EXPIRED, TimeUnit.MILLISECONDS);
BufferedImage bufferedImage = captchaProducer.createImage(captchaText);
try (ServletOutputStream outputStream = response.getOutputStream()){
ImageIO.write(bufferedImage, "jpg", outputStream);
outputStream.flush();
} catch (IOException e) {
log.error("获取图形验证码异常:{}", e);
}
}
简介:注册短信验证码接口开发
@Override
public JsonData sendCode(SendCodeEnum sendCodeType, String to) {
if(CheckUtil.isEmail(to)){
//邮箱验证码
}else if(CheckUtil.isPhone(to)){
//短信验证码
}
return JsonData.buildResult(BizCodeEnum.CODE_TO_ERROR);
}
public class CheckUtil {
/**
* 邮箱正则
*/
private static final Pattern MAIL_PATTERN = Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$");
/**
* 手机号正则
*/
private static final Pattern PHONE_PATTERN = Pattern.compile("^((13[0-9])|(14[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\\d{8}$");
/**
* @param email
* @return
*/
public static boolean isEmail(String email) {
if (null == email || "".equals(email)) {
return false;
}
Matcher m = MAIL_PATTERN.matcher(email);
return m.matches();
}
/**
*
* @param phone
* @return
*/
public static boolean isPhone(String phone) {
if (null == phone || "".equals(phone)) {
return false;
}
Matcher m = PHONE_PATTERN.matcher(phone);
boolean result = m.matches();
return result;
}
}
简介:注册短信验证码防刷方案你能想到几个
需求:一定时间内禁止重复发送短信,大家想下有哪几种实现方式
方式一:前端增加校验倒计时,不到60秒按钮不给点击
方式二:增加Redis存储,发送的时候设置下额外的key,并且60秒后过期
非原子操作,存在不一致性
增加的额外的key - value存储,浪费空间
/**
* 前置:判断是否重复发送
*
* 1、存储验证码到缓存
*
* 2、发送短信验证码
*
* 后置:存储发送记录
**/
方式三:基于原先的key拼装时间戳
简介:注册邮箱验证码防刷落地和整体测试
@Override
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) {
String cacheKey = String.format(RedisKey.CHECK_CODE_KEY,sendCodeEnum.name(),to);
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
//如果不为空,再判断是否是60秒内重复发送 0122_232131321314132
if(StringUtils.isNotBlank(cacheValue)){
long ttl = Long.parseLong(cacheValue.split("_")[1]);
//当前时间戳-验证码发送的时间戳,如果小于60秒,则不给重复发送
long leftTime = CommonUtil.getCurrentTimestamp() - ttl;
if( leftTime < (1000*60)){
log.info("重复发送短信验证码,时间间隔:{}秒",leftTime);
return JsonData.buildResult(BizCodeEnum.CODE_LIMITED);
}
}
String code = CommonUtil.getRandomCode(6);
//生成拼接好验证码
String value = code+"_"+CommonUtil.getCurrentTimestamp();
redisTemplate.opsForValue().set(cacheKey,value,CODE_EXPIRED,TimeUnit.MILLISECONDS);
if(CheckUtil.isEmail(to)){
//发送邮箱验证码 TODO
}else if(CheckUtil.isPhone(to)){
//发送手机验证码
smsComponent.send(to,smsConfig.getTemplateId(),code);
}
return JsonData.buildSuccess();
}
简介:分布式文件存储常见解决方案介绍
目前业界比较多这个解决方案,这边就挑选几个介绍下
是在 Apache License v2.0 下发布的对象存储服务器,学习成本低,安装运维简单,主流语言的客户端整合都有, 号称最强的对象存储文件服务器,且可以和容器化技术docker/k8s等结合,社区活跃但不够成熟,业界参考资料较少
官网:https://docs.min.io/cn/
一个开源的轻量级分布式文件系统,比较少的客户端可以整合,目前主要是C和java客户端,在一些互联网创业公司中有应用比较多,没有官方文档,社区不怎么活跃.
架构+部署结构复杂,出问题定位比较难定位,可以说是fastdfs零件的组装过程,需要去理解fastDFS的架构设计,才能够正确的安装部署
云厂商
阿里云OSS
七牛云
腾讯云
亚马逊云
CDN最强:Akamai https://www.akamai.com/cn
选云厂商理由
选开源MinIO的理由
文件上传流程
简介:阿里云OSS对象存储介绍和开通
对象存储OSS(Object Storage Service)是阿里云提供的海量、安全、低成本、高持久的云存储服务。其数据设计持久性不低于99.9999999999%(12个9),服务设计可用性不低于99.995%。
OSS具有与平台无关的RESTful API接口,您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
提供标准、低频访问、归档和冷归档四种存储类型,全面覆盖从热到冷的各种数据存储场景:
标准存储类型 | 高持久、高可用、高性能的对象存储服务,支持频繁的数据访问。是各种社交、分享类的图片、音视频应用、大型网站、大数据分析的合适选择。 |
---|---|
低频访问存储类型 | 适合长期保存不经常访问的数据(平均每月访问频率1到2次)。存储单价低于标准类型,适合各类移动应用、智能设备、企业数据的长期备份,支持实时数据访问。 |
归档存储类型 | 适合需要长期保存(建议半年以上)的归档数据,在存储周期内极少被访问,数据进入到可读取状态需要1分钟的解冻时间。适合需要长期保存的档案数据、医疗影像、科学资料、影视素材。 |
冷归档存储类型 | 适合需要超长时间存放的极冷数据。例如因合规要求需要长期留存的数据、大数据及人工智能领域长期积累的原始数据、影视行业长期留存的媒体资源、在线教育行业的归档视频等。 |
开通阿里云OSS
有阿里云账号、实名认证
OSS介绍:https://www.aliyun.com/product/oss
OSS控制台:https://oss.console.aliyun.com/bucket
学习路径:https://help.aliyun.com/learn/learningpath/oss.html
开通后的操作
简介:权限知识 RBAC-ACL模式应用之阿里云RAM访问控制
ACL: Access Control List 访问控制列表
RBAC: Role Based Access Control
总结:不能过于复杂,规则过多,维护性和性能会下降, 更多分类 ABAC、PBAC等
RAM权限介绍
阿里云用于各个产品的权限,基于RBAC、ACL模型都有,进行简单管理账号、统一分配权限、集中管控资源,从而建立安全、完善的资源控制体系。
众多产品,一般采用子账号进行分配权限,防止越权攻击
建立用户,勾选编程访问(保存accessKey和accessSecret,只出现一次)
为新建用户授权OSS全部权限
简介:阿里云OSS对象存储客户端集成和测试服务
添加阿里云OSS的SDK
地址:https://help.aliyun.com/document_detail/32008.html
添加maven依赖
<dependency>
<groupId>com.aliyun.ossgroupId>
<artifactId>aliyun-sdk-ossartifactId>
<version>3.10.2version>
dependency>
<dependency>
<groupId>com.aliyun.ossgroupId>
<artifactId>aliyun-sdk-ossartifactId>
dependency>
账号微服务配置OSS
#阿里云OSS配置
aliyun:
oss:
endpoint: oss-cn-guangzhou.aliyuncs.com
access-key-id: LTAI5tHVGvYw7twoVFyruB1H
access-key-secret: r4d0EudzSvPfVMb9Zp0TfmsE32RXmN
bucketname: dcloud-link
@ConfigurationProperties(prefix = "aliyun.oss")
@Configuration
@Data
public class OSSConfig {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketname;
}
开发controller
开发service
@Service
@Slf4j
public class FileServiceImpl implements FileService {
@Autowired
private OSSConfig ossConfig;
@Override
public String uploadUserImg(MultipartFile file) {
//获取相关配置
String bucketname = ossConfig.getBucketname();
String endpoint = ossConfig.getEndpoint();
String accessKeyId = ossConfig.getAccessKeyId();
String accessKeySecret = ossConfig.getAccessKeySecret();
//创建OSS对象
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
//获取原生文件名 xxx.jpg
String originalFileName = file.getOriginalFilename();
//JDK8的日期格式
LocalDateTime ldt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
//拼装路径,oss上存储的路径 user/2022/12/1/sdfdsafsdfdsf.jpg
String folder = dtf.format(ldt);
String fileName = CommonUtil.generateUUID();
String extension = originalFileName.substring(originalFileName.lastIndexOf("."));
// 在OSS上的bucket下创建 user 这个文件夹
String newFileName = "user/"+folder+"/"+fileName+ extension;
try {
PutObjectResult putObjectResult = ossClient.putObject(bucketname,newFileName,file.getInputStream());
//拼装返回路径
if(putObjectResult != null){
String imgUrl = "https://"+bucketname+"."+endpoint+"/"+newFileName;
return imgUrl;
}
} catch (IOException e) {
log.error("文件上传失败:{}",e);
} finally {
//oss关闭服务,不然会造成OOM
ossClient.shutdown();
}
return null;
}
}
简介: 账号微服务头像上传阿里云OSS接口和PostMan测试
/**
* 上传用户头像
*
* 默认文件大小 1M,超过会报错
*
* @param file
* @return
*/
@PostMapping(value = "upload")
public JsonData uploadHeaderImg(@RequestPart("file") MultipartFile file){
String result = fileService.uploadUserHeadImg(file);
return result != null?JsonData.buildSuccess(result):JsonData.buildResult(BizCodeEnum.FILE_UPLOAD_USER_IMG_FAIL);
}
} catch (IOException e) {
log.error("文件上传失败:{}",e);
} finally {
//oss关闭服务,不然会造成OOM
ossClient.shutdown();
}
return null;
}
}
#### 第5集 账号微服务头像上传阿里云OSS接口和PostMan测试
**简介: 账号微服务头像上传阿里云OSS接口和PostMan测试**
- 文件上传流程
- 先上传文件,返回url地址,再和普通表单一并提交(推荐这种,更加灵活,失败率低)
- 文件和普通表单一并提交(设计流程比较多,容易超时和失败)
- 注意:默认SpringBoot最大文件上传是1M,大家测试的时候记得关注下
- 开发controller
- @requestPart注解 接收文件以及其他更为复杂的数据类型
```java
/**
* 上传用户头像
*
* 默认文件大小 1M,超过会报错
*
* @param file
* @return
*/
@PostMapping(value = "upload")
public JsonData uploadHeaderImg(@RequestPart("file") MultipartFile file){
String result = fileService.uploadUserHeadImg(file);
return result != null?JsonData.buildSuccess(result):JsonData.buildResult(BizCodeEnum.FILE_UPLOAD_USER_IMG_FAIL);
}