首页 文章 精选 留言 我的

精选列表

搜索[分布式调度],共10000篇文章
优秀的个人博客,低调大师

从构建分布式秒杀系统聊聊验证码

前言 为了拦截大部分请求,秒杀案例前端引入了验证码。淘宝上很多人吐槽,等输入完秒杀活动结束了,对,结束了...... 当然了,验证码的真正作用是,有效拦截刷单操作,让羊毛党空手而归。 验证码 那么到底什么是验证码呢?验证码作为一种人机识别手段,其终极目的,就是区分正常人和机器的操作。我们常见的互联网注册、登录、发帖、领优惠券、投票等等应用场景,都有被机器刷造成各类损失的风险。 目前常见的验证码形式多为图片验证码,即数字、字母、文字、图片物体等形式的传统字符验证码。这类验证码看似简单易操作,但实际用户体验较差(参见12306网站),且随着OCR技术和打码平台的利用,图片比较容易被破解,被破解之后就形同虚设。 这里我们使用腾讯的智能人机安全验证码,告别传统验证码的单点防御,十道安全栅栏打造立体全面的安全验证,将黑产拒之门外。 场景 下面我们来瞅瞅验证码轻松解决了那些场景安全问题: 登录注册,为你防护撞库×××、阻止注册机批量注册 活动秒杀,有效拦截刷单操作,让羊毛党空手而归 点赞发帖,有效解决广告屠版、恶意灌水、刷票问题 数据保护,防止自动机、爬虫盗取网页内容和数据 申请 申请地址:https://007.qq.com/product.html 在线体验:https://007.qq.com/online.html 只要一个QQ就可以免费申请,对于一般的企业OA系统或者个人博客网站,验证码免费套餐足够了已经,具备以下特点: 2000次/小时安全防护 支持免验证+分级验证 三分钟快速接入 全功能配置后台 支持HTTPS 阈值内流量无广告 2000次/小时的安全防护,一般很少达到如此效果,当然了即时超出阈值,顶多也就是多个广告而已。 接入 快读接入:https://007.qq.com/quick-start.html 接入与帮助提供了多种客户端和服务端的接入案例,这里我们使用我们秒杀案例中最熟悉的Java语言来接入。 前端 引入JS: <script src="https://ssl.captcha.qq.com/TCaptcha.js"></script> 页面元素: <!--点击此元素会自动激活验证码,不一定是button,其他标签也可以--> <!--id : 元素的id(必须)--> <!--data-appid : AppID(必须)--> <!--data-cbfn : 回调函数名(必须)--> <!--data-biz-state : 业务自定义透传参数(可选)--> <button id="TencentCaptcha" data-appid="*********" data-cbfn="callback">验证</button> JS回调: <script type="text/javascript"> window.callback = function(res){ console.log(res) // res(未通过验证)= {ret: 1, ticket: null} // res(验证成功) = {ret: 0, ticket: "String", randstr: "String"} if(res.ret === 0){ startSeckill(res) } } //后台验证ticket,并进入秒杀队列 function startSeckill(res){ $.ajax({ url : "startSeckill", type : 'post', data : {'ticket' : res.ticket,'randstr':res.randstr}, success : function(result) { //验证是否通过,提示用户 } }); } </script> 后端 @Api(tags = "秒杀商品") @RestController @RequestMapping("/seckillPage") public class SeckillPageController { @Autowired private ActiveMQSender activeMQSender; //自定义工具类 @Autowired private HttpClient httpClient; //这里自行配置参数 @Value("${qq.captcha.url}") private String url; @Value("${qq.captcha.aid}") private String aid; @Value("${qq.captcha.AppSecretKey}") private String appSecretKey; @RequestMapping("/startSeckill") public Result startSeckill(String ticket,String randstr,HttpServletRequest request) { HttpMethod method =HttpMethod.POST; MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>(); params.add("aid", aid); params.add("AppSecretKey", appSecretKey); params.add("Ticket", ticket); params.add("Randstr", randstr); params.add("UserIP", IPUtils.getIpAddr(request)); String msg = httpClient.client(url,method,params); /** * response: 1:验证成功,0:验证失败,100:AppSecretKey参数校验错误[required] * evil_level:[0,100],恶意等级[optional] * err_msg:验证错误信息[optional] */ //{"response":"1","evil_level":"0","err_msg":"OK"} JSONObject json = JSONObject.parseObject(msg); String response = (String) json.get("response"); if("1".equals(response)){ //进入队列、假数据而已 Destination destination = new ActiveMQQueue("seckill.queue"); activeMQSender.sendChannelMess(destination,1000+";"+1); return Result.ok(); }else{ return Result.error("验证失败"); } } } 自定义请求工具类 HttpClient: @Service public class HttpClient { public String client(String url, HttpMethod method, MultiValueMap<String, String> params){ RestTemplate client = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); // 请勿轻易改变此提交方式,大部分的情况下,提交方式都是表单提交 headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(params, headers); // 执行HTTP请求 ResponseEntity<String> response = client.exchange(url, HttpMethod.POST, requestEntity, String.class); return response.getBody(); } } 获取IP地址工具类 IPUtils : /** * IP地址 */ public class IPUtils { private static Logger logger = LoggerFactory.getLogger(IPUtils.class); /** * 获取IP地址 * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 */ public static String getIpAddr(HttpServletRequest request) { String ip = null; try { ip = request.getHeader("x-forwarded-for"); if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } } catch (Exception e) { logger.error("IPUtils ERROR ", e); } // 使用代理,则获取第一个IP地址 if (StringUtils.isEmpty(ip) && ip.length() > 15) { if (ip.indexOf(",") > 0) { ip = ip.substring(0, ip.indexOf(",")); } } return ip; } } 案例效果图 启动项目访问:http://localhost:8080/seckill/1000.shtml 定制接入 在系统登录的时候,我们需要先校验用户名以及密码,然后调用验证码操作,这里就需要我们定制接入了。 <!-- 项目中使用了Vue --> <div class="log_btn" @click="login" >登录</div> login: function () { //这里校验用户名以及密码 // 直接生成一个验证码对象 var captcha = new TencentCaptcha('2001344788', function(res) { if(res.ret === 0){//回调成功 var data = {'username':username,'password':password,'ticket':res.ticket,'randstr':res.randstr} $.ajax({ type: "POST", url: "sys/loginCaptcha", data: data, dataType: "json", success: function(result){ //校验是否成功 } }); } }); captcha.show(); // 显示验证码 }, 后台监控 腾讯后台还提供了简单实用的数据监控,如下: 小结 总体来说,系统接入人机验证码还是很方便的,并没有技术难点,难点已经被提供商封装,我们只需要简单的调用即可。 秒杀案例:https://gitee.com/52itstyle/spring-boot-seckill 演示案例(点击生成按钮):http://jichou.52itstyle.com

优秀的个人博客,低调大师

SpringBoot 整合 Dubbo&amp;Zookeeper 实现分布式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36367789/article/details/81665692 1. 安装 Zookeeper 环境 Zookeeper 环境搭建&zk命令详解 2. 服务提供者 因为用了父工程的版本管理,所以这里没有显示版本,我把用到的版本给大家分享下。 jdk 1.8 springboot 1.5.8 spring-boot-starter-dubbo 1.0.0 闲余之际我会把demo源码分享。 2.1 pom依赖 <dependencies> <!-- Spring Boot Web 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Test 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--spring data jpa --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.8-dmr</version> <scope>compile</scope> </dependency> <!--dubbo 依赖--> <dependency> <groupId>io.dubbo.springboot</groupId> <artifactId>spring-boot-starter-dubbo</artifactId> </dependency> <!--junit 测试工具--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> 2.2. Service填写 大概给下项目架构: 为了方便,我用的springboot data jpa做持久性框架。如果不会jpa,请先看我springboot jpa整合。 1. SchoolRepository .java /** * Created by Fant.J. */ @Repository public interface SchoolRepository extends JpaRepository<School,Integer> { } SchoolService.java 略(一个正常的借口) 实现类SchoolServiceImpl .java核心代码 @Service(version = "2.0.1") public class SchoolServiceImpl implements SchoolService { @Autowired private SchoolRepository schoolRepository; 这里的@Service注解是dubbo的注解,不是springframework下的注解。该注解就是向zk注册服务。 2.3. application.properties server.port=9002 ## Dubbo 服务提供者配置 spring.dubbo.application.name=school-server spring.dubbo.registry.address=zookeeper://xxx.xxx.xxx.xxx:2181 spring.dubbo.protocol.name=dubbo spring.dubbo.protocol.port=20882 spring.dubbo.scan=com.xxx.school.service spring.datasource.url=jdbc:mysql://xxxxxxxxxx spring.datasource.username=xxxx spring.datasource.password=xxxx spring.datasource.driver-class-name=com.mysql.jdbc.Driver 注意: 每一个服务都需要一个未被使用的dubbo端口 。 3. 服务消费 为了和服务提供者解耦,我们需要把Service接口类单独拿出来放到client模块里,这里不贴详细代码了。 3.1 pom.xml 这里略,根据controller类里的需要来填充相关依赖 3.2 SchoolController @RestController @RequestMapping("/sch") public class SchoolController { @Reference(version = "2.0.1") private SchoolService schoolService; @RequestMapping("/all") public ServerResponse getAll(){ return schoolService.selectAll(); } } 注意与@Service注解的version属性值一一对应。 3.3 application.properties ## Dubbo 服务消费者配置 spring.dubbo.application.name=xxx spring.dubbo.registry.address=zookeeper://xxxx.xxx.xxx.xxx spring.dubbo.scan=com.xxx.web.controller 成功截图: 有疑问请在下面留言。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册