赞
踩
简介:介绍Mybatis-plus-generator代码自动化生成工具
介绍
基础版mybatis-genarator
进阶版mybatis-plus-genarator
添加依赖
<!-- 代码自动生成依赖 begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<!-- 代码自动生成依赖 end-->
public class MyBatisPlusGenerator { public static void main(String[] args) { //1. 全局配置 GlobalConfig config = new GlobalConfig(); // 是否支持AR模式 config.setActiveRecord(true) // 作者 .setAuthor("二当家小D") // 生成路径,最好使用绝对路径,window路径是不一样的 //TODO TODO TODO TODO .setOutputDir("/Users/xdclass/Desktop/demo/src/main/java") // 文件覆盖 .setFileOverride(true) // 主键策略 .setIdType(IdType.AUTO) .setDateType(DateType.ONLY_DATE) // 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的 .setServiceName("%sService") //实体类结尾名称 .setEntityName("%sDO") //生成基本的resultMap .setBaseResultMap(true) //不使用AR模式 .setActiveRecord(false) //生成基本的SQL片段 .setBaseColumnList(true); //2. 数据源配置 DataSourceConfig dsConfig = new DataSourceConfig(); // 设置数据库类型 dsConfig.setDbType(DbType.MYSQL) .setDriverName("com.mysql.cj.jdbc.Driver") //TODO TODO TODO TODO .setUrl("jdbc:mysql://127.0.0.1:3306/xdclass_user?useSSL=false") .setUsername("root") .setPassword("xdclass.net"); //3. 策略配置globalConfiguration中 StrategyConfig stConfig = new StrategyConfig(); //全局大写命名 stConfig.setCapitalMode(true) // 数据库表映射到实体的命名策略 .setNaming(NamingStrategy.underline_to_camel) //使用lombok .setEntityLombokModel(true) //使用restcontroller注解 .setRestControllerStyle(true) // 生成的表, 支持多表一起生成,以数组形式填写 //TODO TODO TODO TODO .setInclude("user","address"); //4. 包名策略配置 PackageConfig pkConfig = new PackageConfig(); pkConfig.setParent("net.xdclass") .setMapper("mapper") .setService("service") .setController("controller") .setEntity("model") .setXml("mapper"); //5. 整合配置 AutoGenerator ag = new AutoGenerator(); ag.setGlobalConfig(config) .setDataSource(dsConfig) .setStrategy(stConfig) .setPackageInfo(pkConfig); //6. 执行操作 ag.execute(); System.out.println("======= 小滴课堂 Done 相关代码生成完毕 ========"); } }
简介:项目编码规范介绍和POJO实体类约定
一方库: 本工程内部子项目模块依赖的库(jar 包)。
二方库: 公司内部发布到中央仓库,可供公司内部其它应用依赖的库(jar包)。
三方库: 公司之外的开源库(jar 包)。
POJO(Plain Ordinary Java Object): 在手册中,POJO 专指只有 setter / getter / toString的简单类,包括DO/DTO/BO/VO等, 禁止命名成xxxPOJO
A) Service/DAO层方法命名规约
1) 获取单个对象的方法用get做前缀。
2) 获取多个对象的方法用list做前缀,复数形式结尾如:listObjects。
3) 获取统计值的方法用count做前缀。
4) 插入的方法用save/insert做前缀。
5) 删除的方法用remove/delete做前缀。
6) 修改的方法用update做前缀。
B) 领域模型命名规约
1) 数据对象:xxxDO,xxx即为数据表名。
2) 一般数据传输对象:xxxDTO,xxx为业务领域相关的名称,项目里面也用VO。
3) 展示对象:xxxVO,也就是响应给前端的实体包装类。
4) 接收前端json对象请求的命名为 XXXRequest
简介:用户微服务数据库配置和查询个人收货地址接口
server: port: 9001 spring: application: name: xdclass-user-service #数据库配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/xdclass_user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: xdclass.net #配置plus打印sql日志 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #设置日志级别,ERROR/WARN/INFO/DEBUG,默认是INFO以上才显示 logging: level: root: INFO
@SpringBootApplication
@MapperScan("net.xdclass.mapper")
简介:微服务整合SwaggerUI3.0接口文档
Postman配置多环境操作
common项目 新增SwaggerUI3.0依赖,parent项目已经声明了版本
<!--swagger ui接口文档依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
@Component @EnableOpenApi @Data public class SwaggerConfiguration { @Bean public Docket webApiDoc(){ return new Docket(DocumentationType.OAS_30) .groupName("用户端接口文档") .pathMapping("/") // 定义是否开启swagger,false为关闭,可以通过变量控制,线上关闭 .enable(true) //配置api文档元信息 .apiInfo(apiInfo()) // 选择哪些接口作为swagger的doc发布 .select() .apis(RequestHandlerSelectors.basePackage("net.xdclass")) //正则匹配请求路径,并分配至当前分组 .paths(PathSelectors.ant("/api/**")) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("1024电商平台") .description("微服务接口文档") .contact(new Contact("小滴课堂-二当家小D", "https://xdclass.net", "794666918@qq.com")) .version("12") .build(); }
简介:SwaggerUI3.0接口文档分组和Header头定义
@Bean public Docket adminApiDoc(){ return new Docket(DocumentationType.OAS_30) .groupName("管理端接口文档") .pathMapping("/") // 定义是否开启swagger,false为关闭,可以通过变量控制,线上关闭 .enable(true) //配置api文档元信息 .apiInfo(apiInfo()) // 选择哪些接口作为swagger的doc发布 .select() .apis(RequestHandlerSelectors.basePackage("net.xdclass")) //正则匹配请求路径,并分配至当前分组 .paths(PathSelectors.ant("/admin/**")) .build(); }
@Bean public Docket webApiDoc(){ return new Docket(DocumentationType.OAS_30) .groupName("用户端接口文档") .pathMapping("/") // 定义是否开启swagger,false为关闭,可以通过变量控制,线上关闭 .enable(true) //配置api文档元信息 .apiInfo(apiInfo()) // 选择哪些接口作为swagger的doc发布 .select() .apis(RequestHandlerSelectors.basePackage("net.xdclass")) //正则匹配请求路径,并分配至当前分组 .paths(PathSelectors.ant("/api/**")) //正则匹配请求路径,并分配至当前分组,当前所有接口 .paths(PathSelectors.any()) .build() //新版swagger3.0配置 .globalRequestParameters(getGlobalRequestParameters()) .globalResponses(HttpMethod.GET, getGlobalResponseMessage()) .globalResponses(HttpMethod.POST, getGlobalResponseMessage()); } /** * 生成全局通用参数, 支持配置多个响应参数 * @return */ private List<RequestParameter> getGlobalRequestParameters() { List<RequestParameter> parameters = new ArrayList<>(); parameters.add(new RequestParameterBuilder() .name("token") .description("登录令牌") .in(ParameterType.HEADER) .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING))) .required(false) .build()); // parameters.add(new RequestParameterBuilder() // .name("version") // .description("版本号") // .required(true) // .in(ParameterType.HEADER) // .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING))) // .required(false) // .build()); return parameters; } /** * 生成通用响应信息 * @return */ private List<Response> getGlobalResponseMessage() { List<Response> responseList = new ArrayList<>(); responseList.add(new ResponseBuilder().code("4xx").description("请求错误,根据code和msg检查").build()); return responseList; } }
简介:统一接口响应协议和响应工具类封装
/** * 小滴课堂,愿景:让技术不再难学 * * @Description 状态码定义约束,共6位数,前三位代表服务,后4位代表接口 * 比如 商品服务210,购物车是220、用户服务230,403代表权限 * **/ public enum BizCodeEnum { /** * 通用操作码 */ OPS_REPEAT(110001,"重复操作"), /** *验证码 */ CODE_TO_ERROR(240001,"接收号码不合规"), CODE_LIMITED(240002,"验证码发送过快"), CODE_ERROR(240003,"验证码错误"), CODE_CAPTCHA(240101,"图形验证码错误"), /** * 账号 */ ACCOUNT_REPEAT(250001,"账号已经存在"), ACCOUNT_UNREGISTER(250002,"账号不存在"), ACCOUNT_PWD_ERROR(250003,"账号或者密码错误"); }
@Data @AllArgsConstructor @NoArgsConstructor public class JsonData { /** * 状态码 0 表示成功,1表示处理中,-1表示失败 */ private Integer code; /** * 数据 */ private Object data; /** * 描述 */ private String msg; /** * 成功,传入数据 * @return */ public static JsonData buildSuccess() { return new JsonData(0, null, null); } /** * 成功,传入数据 * @param data * @return */ public static JsonData buildSuccess(Object data) { return new JsonData(0, data, null); } /** * 失败,传入描述信息 * @param msg * @return */ public static JsonData buildError(String msg) { return new JsonData(-1, null, msg); } /** * 自定义状态码和错误信息 * @param code * @param msg * @return */ public static JsonData buildCodeAndMsg(int code, String msg) { return new JsonData(code, null, msg); } /** * 传入枚举,返回信息 * @param codeEnum * @return */ public static JsonData buildResult(BizCodeEnum codeEnum){ return JsonData.buildCodeAndMsg(codeEnum.getCode(),codeEnum.getMessage()); } }
简介:自定义全局异常+处理器开发
/** * 全局异常处理 */ @Data public class BizException extends RuntimeException { private Integer code; private String msg; public BizException(Integer code, String message) { super(message); this.code = code; this.msg = message; } public BizException(BizCodeEnum bizCodeEnum) { super(bizCodeEnum.getMsg()); this.code = bizCodeEnum.getCode(); this.msg = bizCodeEnum.getMsg(); } }
@ControllerAdvice @Slf4j public class ExceptionHandle { @ExceptionHandler(value = Exception.class) @ResponseBody public JsonData Handle(Exception e) { if (e instanceof BizException) { BizException bizException = (BizException) e; log.info("[业务异常]{}", e); return JsonData.buildError(bizException.getMsg(), bizException.getCode()); } else { log.info("[系统异常]{}", e); return JsonData.buildError("全局异常,未知错误"); } } }
简介:微服务项目集成Spring Boot Test单元测试
Spring Boot Test 是在Spring Test之上的再次封装, 使用@SpringBootTest后,Spring将加载所有被管理的bean,等同于启动了整个服务
common项目添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
@RunWith(SpringRunner.class) @SpringBootTest(classes = UserApplication.class) @Slf4j public class AddressTest { @Autowired private AddressService addressService; @Test public void testAddressDetail(){ AddressDO addressDO = addressService.detail(1L); log.info(addressDO.toString()); } }
#配置plus打印sql日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
简介:介绍微服务注册功能和流程介绍
功能需求
功能演示
安全需求
针对每个功能,初级开发和高级开发的思路是不一样
简介:从邮箱短信验证码介绍老一代的轰炸机
手机短信轰炸机是批量、循环给手机无限发送各种网站的注册验证码短信的方法。
最早发明是用来整治街头广告电话号泛滥的一种手段,采用“手机短信轰炸机”软件可无限发送垃圾短信到牛皮癣小广告的手机号码上,使对方的手机快速消耗电量,变成高频率振动棒,且无法正常使用。
“短信轰炸机”可严厉打击城市“牛皮癣”,还城市明净容颜。
某次大型程序员相亲现场-老王得罪了小王, 小王不爽,就道听途说知道了”短信轰炸机“,1天50元,轰炸了5天还打折300元。
一天内接到来自全国各地数千个陌生电话短信的轰炸骚扰,导致个人通讯中断,被工作生活受到严重影响,连刚相亲到的女友没没法联系上了。
⚠️郑重声明⚠️:本集内容只限于小滴课堂用于教学提高业务系统安全性,切勿使用课程思路进行攻击破坏或者获取利益,如产生的一切后果与本人和所属公司无关。
《刑法》第二百八十五条 【非法侵入计算机信息系统罪;非法获取计算机信息系统数据、非法控制计算机信息系统罪】违反国家规定,侵入国家事务、国防建设、尖端科学技术领域的计算机信息系统的,处三年以下有期徒刑或者拘役。
违反国家规定,侵入前款规定以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,或者对该计算机信息系统实施非法控制,情节严重的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。
【提供侵入、非法控制计算机信息系统程序、工具罪】提供专门用于侵入、非法控制计算机信息系统的程序、工具,或者明知他人实施侵入、非法控制计算机信息系统的违法犯罪行为而为其提供程序、工具,情节严重的,依照前款的规定处罚
天网恢恢,疏而不漏,一切操作都是可以被追寻到的,ip/设备/网络/基站/监控等等,总有你想不到的方式抓到你
很多人都用手机注册一些网站的验证了,比如手机验证码。先填手机号,然后发一条验证码过去,输入验证码,完成验证,注册成功。
* 寻找大量肉鸡网站,寻找发送验证码的请求接口
* 如果找不到接口,也可以使用自动化UI工具触发
* 编写程序和调度任务,相关脚本录入数据库
* 输入目标手机号或者邮箱,触发攻击
简介:开发人员和灰色产业的那些事情
请大家先看一个自动化脚本技术视频
案例二:你需要考虑的是批量注册账号
朋友圈、群经常能看到某平台上的点赞、刷粉等业务,比如某d音、某w信
很大程度就是平台一开始研发的时候,没关注安全账号体系,
灰色产业嗅觉灵敏:时刻顶着大平台的产品,一出现就会大量注册账号(因为安全防范最弱),公司看到注册用户指数级上涨更开心
谁能知道,BAT的大厂的产品 几十亿甚至几百亿账号里面有多少是僵尸号
如何避免自己的网站成为”肉鸡“或者被刷呢
是否可以一劳永逸???
没有百分百的安全,验证码是可以破解的,ip也是可以租用代理ip的
攻防永远是有的,只过加大了攻击者的成本,ROI划不过来自然就放弃了
简介:谷歌开源kaptcha图形验证码开发
Kaptcha 框架介绍 谷歌开源的一个可高度配置的实用验证码生成工具
聚合工程依赖添加(使用国内baomidou二次封装的springboot整合starter)
<!--kaptcha依赖包-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
</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; } }
简介:Linux环境下docker部署+redis安装
#依次运行以下命令添加yum源 yum update yum install epel-release -y yum clean all yum list #安装并运行Docker。 yum install docker-io -y systemctl start docker #检查安装结果。 docker info #启动使用Docker systemctl start docker #运行Docker守护进程 systemctl stop docker #停止Docker守护进程 systemctl restart docker #重启Docker守护进程 #修改镜像仓库 vim /etc/docker/daemon.json #改为下面内容,然后重启docker { "debug":true,"experimental":true, "registry-mirrors":["https://pb5bklzr.mirror.aliyuncs.com","https://hub-mirror.c.163.com","https://docker.mirrors.ustc.edu.cn"] } #查看信息 docker info
docker部署redis 并配置密码
docker run -itd --name xdclass-redis -p 8000:6379 redis --requirepass 123456
Options | Mean |
---|---|
-i | 以交互模式运行容器,通常与 -t 同时使用; |
-t | 为容器重新分配一个伪输入终端,通常与 -i 同时使用; |
-d | 后台运行容器,并返回容器ID; |
简介:用户微服务开发图形验证码接口
redis做隔离, 多集群:核心集群和非核心集群,高并发集群和非高并发集群
大家一定要有自己的工具集
用户微服务配置Redis
spring:
application:
name: xdclass-user-service
redis:
host: 112.74.55.160
password: 123456
port: 8000
<!--redis客户端-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
/** *临时使用10分钟有效,方便测试 */ private static final long CAPTCHA_CODE_EXPIRED = 60 * 1000 * 10; /** * 获取图形验证码 * @param request * @param response */ @ApiOperation("获取图形验证码") @GetMapping("captcha") public void getCaptcha(HttpServletRequest request, HttpServletResponse response){ String cacheKey = getCaptchaKey(request); String capText = captchaProducer.createText(); //存储 redisTemplate.opsForValue().set(cacheKey,capText,CAPTCHA_CODE_EXPIRED,TimeUnit.MILLISECONDS); BufferedImage bi = captchaProducer.createImage(capText); ServletOutputStream out = null; try { response.setDateHeader("Expires", 0); response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); response.addHeader("Cache-Control", "create_date-check=0, pre-check=0"); response.setHeader("Pragma", "no-cache"); out = response.getOutputStream(); ImageIO.write(bi, "jpg", out); out.flush(); out.close(); } catch (IOException e) { log.error("获取验证码失败:{}",e); } }
/** * 获取ip * @param request * @return */ public static String getIpAddr(HttpServletRequest request) { String ipAddress = null; try { ipAddress = request.getHeader("x-forwarded-for"); if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("WL-Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getRemoteAddr(); if (ipAddress.equals("127.0.0.1")) { // 根据网卡取本机配置的IP InetAddress inet = null; try { inet = InetAddress.getLocalHost(); } catch (UnknownHostException e) { e.printStackTrace(); } ipAddress = inet.getHostAddress(); } } // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length() // = 15 if (ipAddress.indexOf(",") > 0) { ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); } } } catch (Exception e) { ipAddress=""; } return ipAddress; } public static String MD5(String data) { try { java.security.MessageDigest md = MessageDigest.getInstance("MD5"); byte[] array = md.digest(data.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte item : array) { sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb.toString().toUpperCase(); } catch (Exception exception) { } return null; }
简介:讲解发送邮件的基础知识和项目整合邮箱配置
账号准备和配置
user-service项目配置添加依赖
<!--发送邮件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
#邮箱服务配置
mail:
host: smtp.126.com #发送邮件服务器
username: waitforxy@126.com #发送邮件的邮箱地址
password: CJVYSJOTRXUSSEJE #客户端授权码,不是邮箱密码,网易的是自己设置的
from: waitforxy@126.com # 发送邮件的地址,和上面username一致
properties.mail.smtp.starttls.enable: true
properties.mail.smtp.starttls.required: true
properties.mail.smtp.ssl.enable: true
default-encoding: utf-8
@Service @Slf4j public class MailServiceImpl implements MailService { /** * Spring Boot 提供了一个发送邮件的简单抽象,直接注入即可使用 */ @Autowired private JavaMailSender mailSender; /** * 配置文件中的发送邮箱 */ @Value("${spring.mail.from}") private String from; @Override public void sendSimpleMail(String to, String subject, String content) { //创建SimpleMailMessage对象 SimpleMailMessage message = new SimpleMailMessage(); //邮件发送人 message.setFrom(from); //邮件接收人 message.setTo(to); //邮件主题 message.setSubject(subject); //邮件内容 message.setText(content); //发送邮件 mailSender.send(message); log.info("邮件发成功:{}",message.toString()); } }
简介:注册邮箱验证码接口开发
/** * 支持手机号、邮箱发送验证码 * @return */ @ApiOperation("发送验证码") @GetMapping("send_code") public JsonData sendRegisterCode(@ApiParam("收信人") @RequestParam(value = "to", required = true)String to, @ApiParam("图形验证码") @RequestParam(value = "captcha", required = true)String captcha, HttpServletRequest request){ String key = getCaptchaKey(request); String cacheCaptcha = redisTemplate.opsForValue().get(key); if(captcha!=null && cacheCaptcha!=null && cacheCaptcha.equalsIgnoreCase(captcha)) { redisTemplate.delete(key); JsonData jsonData = notifyService.sendCode(SendCodeEnum.USER_REGISTER,to); return jsonData; }else { return JsonData.buildResult(BizCodeEnum.CODE_CAPTCHA); } }
@Override
public JsonData sendCode(SendCodeEnum sendCodeType, String to) {
if(CheckUtil.isEmail(to)){
//邮箱验证码
mailService.sendSimpleMail(to,SUBJECT,String.format(CONTENT,code));
return JsonData.buildSuccess();
}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])|(15[^4,\\D])|(18[0,5-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); return m.matches(); } }
简介:注册邮箱验证码防刷方案你能想到几个
需求:一定时间内禁止重复发送邮件,大家想下有哪几种实现方式
方式一:前端增加校验倒计时,不到60秒按钮不给点击
方式二:增加Redis存储,发送的时候设置下额外的key,并且60秒后过期
非原子操作,存在不一致性
增加的额外的key - value存储,浪费空间
/**
* 前置:判断是否重复发送
*
* 1、存储验证码到缓存
*
* 2、发送邮箱验证码
*
* 后置:存储发送记录
**/
方式三:基于原先的key拼装时间戳
简介:注册邮箱验证码防刷落地和整体测试
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) { String cacheKey = String.format(CacheKey.CHECK_CODE_KEY,sendCodeEnum.name(),to); String cacheValue = redisTemplate.opsForValue().get(cacheKey); //如果不为空,则判断是否60秒内重复发送 if(StringUtils.isNotBlank(cacheValue)){ long ttl = Long.parseLong(cacheValue.split("_")[1]); //当前时间戳-验证码发送时间戳,如果小于60秒,则不给重复发送 if(CommonUtil.getCurrentTimestamp() - ttl < 1000*60){ log.info("重复发送验证码,时间间隔:{} 秒",(CommonUtil.getCurrentTimestamp()-ttl)/1000); return JsonData.buildResult(BizCodeEnum.CODE_LIMITED); } } //拼接验证码 2322_324243232424324 String code = CommonUtil.getRandomCode(6); String value = code+"_"+CommonUtil.getCurrentTimestamp(); redisTemplate.opsForValue().set(cacheKey,value,CODE_EXPIRED,TimeUnit.MILLISECONDS); if(CheckUtil.isEmail(to)){ //邮箱验证码 mailService.sendMail(to,SUBJECT,String.format(CONTENT,code)); return JsonData.buildSuccess(); } else if(CheckUtil.isPhone(to)){ //短信验证码 } return JsonData.buildResult(BizCodeEnum.CODE_TO_ERROR); }
先的key拼装时间戳
* 好处:满足了当前节点内的原子性,也满足业务需求
简介:注册邮箱验证码防刷落地和整体测试
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) { String cacheKey = String.format(CacheKey.CHECK_CODE_KEY,sendCodeEnum.name(),to); String cacheValue = redisTemplate.opsForValue().get(cacheKey); //如果不为空,则判断是否60秒内重复发送 if(StringUtils.isNotBlank(cacheValue)){ long ttl = Long.parseLong(cacheValue.split("_")[1]); //当前时间戳-验证码发送时间戳,如果小于60秒,则不给重复发送 if(CommonUtil.getCurrentTimestamp() - ttl < 1000*60){ log.info("重复发送验证码,时间间隔:{} 秒",(CommonUtil.getCurrentTimestamp()-ttl)/1000); return JsonData.buildResult(BizCodeEnum.CODE_LIMITED); } } //拼接验证码 2322_324243232424324 String code = CommonUtil.getRandomCode(6); String value = code+"_"+CommonUtil.getCurrentTimestamp(); redisTemplate.opsForValue().set(cacheKey,value,CODE_EXPIRED,TimeUnit.MILLISECONDS); if(CheckUtil.isEmail(to)){ //邮箱验证码 mailService.sendMail(to,SUBJECT,String.format(CONTENT,code)); return JsonData.buildSuccess(); } else if(CheckUtil.isPhone(to)){ //短信验证码 } return JsonData.buildResult(BizCodeEnum.CODE_TO_ERROR); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。