springcloud注册demo(使⽤第⼀代Spring Cloud核⼼组件完成项⽬构建、编码及测试)
一、业务描述
以注册、登录为主线,串联起验证码生成及校验、邮件发送、IP防暴刷、用户统一认证等功能。
实现需基于Spring Cloud 微服务架构,技术涉及Nginx、Eureka、Feign(Ribbon、Hystrix)、Gateway、Config+Bus等。
1.1 注册
1)用户访问到登录页面,在登录页面中有注册新账号功能
2)点击“注册新账号“,跳转到注册页面
3)在注册页面,需要用户输入邮箱地址、密码、确认密码,然后点击”获取验证码“,系统会生成验证码并向所输入的邮箱地址发送该验证码,用户拿到邮箱中的验证码输入后完成注册
规则如下
A:一分钟内只允许获取一次验证码(前端Js控制即可),验证码为随机生成的6位数字,10分钟内有效,验证码存储到mysql数据库中(也可以选择存入到Redis中);
B:存储到mysql数据库之后,使用发邮件功能,将该验证码发送到所输入的邮箱地址中
C:用户从邮箱中拿到验证码,点击注册时,需要进行行校验,因为验证码已经存入数据库,此时只需要查询数据库中该邮箱地址对应的最近一次的验证码记录,校验验证码是否正确,是否超时,若有问题,准确提示给用户
4)注册成功后,根据 <用户邮箱+密码> 生成签发token令牌(此处生成一个UUID模拟即可),该token令牌存入数据库(也可以选择存入到Redis中),并写入cookie中(以后的每次请求都会在cookie中携带该token,网关过滤器通过验证token的合法性来确定用户请求是否合法,如果token合法,根据token取出用户信息---->邮箱),最后重定向到欢迎⻚页面(显示邮箱地址)
1.2 登录
1)用户访问登录页面,在登录页面输入邮箱地址+密码
2)点击登录,后台对用户名和密码进行验证,然后根据<用户邮箱+密码> 生成签发token令牌(此处生成一个UUID模拟即可),该token令牌存入数据库(因为大家未系统学习Redis,所以此处令牌存入数据库即可),并写入cookie中(以后的每次请求都会在cookie中携带该token,网关过滤器通过验证token的合法性来确定用户请求是否合法,如果token合法,根据token取出用户信息---->邮箱),最后重定向到欢迎页面(显示邮箱地址)
1.3 架构描述
Nginx
占⽤用端⼝口:80
实现动静分离。将静态资源 html 页面存放至本地磁盘,数据请求统一经过 GateWay 网关路由到下游微服务。静态资源(html页面)
访问前缀:/static/xxx.html
包括登录页面 login.html、注册页面 register.html、以及成功登录之后的欢迎页面welcome.html,各个页面细节元素后面有描述GateWay 网关
占用端口:9002端口 数据请求前缀:/api/xxx
完成统一路由、IP防暴刷(限制单个客户端IP在最近X分钟内请求注册接口不能超过Y次)、统一认证(登录时验证用户名密码是否合法,合法调用用户微服务生成token,写入cookie,并且携带邮箱地址重定向到欢迎页面;后续请求再到来时,验证客户端请求cookie中携带的token是否合法,合法则放行,此处不考虑token更新问题)等功能路路径路路由规则:
/api/user/** 路由到用户微服务
/api/code/** 路由到验证码微服务
/api/email/** 路由到邮件微服务dabing-service-user 用户微服务
占用端口:8080 数据请求前缀:/api/user/**
提供注册接口、用户是否已注册接口、登录接口(⽣生成token并入库,token写入cookie中)、查
询用户登录邮箱接口等dabing-service-code 验证码微服务
占用端口:8081 数据请求前缀:/api/code/**
用于提供验证码生成、验证码校验等接口,同时调用邮件微服务发送验证码dabing-service-email 邮件微服务
占用端口:8082 数据请求前缀:/api/email/**
提供邮件发送功能,用于将生成的验证码发送到注册邮箱Spring Cloud Config+Bus
占用端口: 9006
共享的配置:数据库连接信息、邮件发送相关配置、IP防暴暴刷指标参数(X分钟的X,Y上限的Y)
注意:除去Eureka是2个实例的集群模式,其他保持单实例
1.4 接口描述
涉及到的微服务名称定义、接口定义、参数名称,可以和下文提到的保持一致。
微服务名称 | API 接口 | 返回值 | 接口描述 |
---|---|---|---|
dabing-service-use | /user/register/{email}/{password}/{code} | true/false | 注册接⼝口,true成功,false失败 |
/user/isRegistered/{email} | true/false | 是否已注册,根据邮箱判断,true代表已经注册过,false代表尚未注册 | |
/user/login/{email}/{password} | 邮箱地址 | 登录接⼝口,验证⽤用户名密码合法性,根据⽤用户名和密码⽣生成token,token存⼊入数据库,并写⼊入cookie中,登录成功返回邮箱地址,重定向到欢迎⻚页 | |
/user/info/{token} | 根据token查询⽤用户登录邮箱接⼝口 | ||
dabing-service-code | /code/create/{email} | true/false | ⽣生成验证码并发送到对应邮箱,成功true,失败false |
/code/validate/{email}/{code} | 0/1/2 | 校验验证码是否正确,0正确1错误2超时 | |
dabing-service-email | /email/{email}/{code} | true/false | 发送验证码到邮箱,true成功,false失败 |
1.5 页面描述
涉及到界面的,界面样式不做要求,界面元素完备即可。
登录界面 login.html
登录界面包含输入项:
邮箱:email
密码:password
点击“登录”调用登录接口,成功后重定向到欢迎页面welcome.html
ajax调用http://localhost:9002/api/user/login/{email}/{password}
点击“注册新账号”可跳转到注册页面register.html注册界面 register.html
注册界面包含输入项:
邮箱:email
密码: password
确认密码:ConfirmPassword
验证码:code
点击“获取验证码”:调用获取验证码接口 http://localhost:9002/code/create/{email}
点击”注册“:调用注册接口 http://localhost:9002/user/register/{email}/{password}/{code}欢迎页面 welcome.html
如下显示登录账户信息即可
1.6 关于网关和配置中心的说明
- GateWay网关
- 统一路由,数据请求的统一出入口
- 添加全局过滤器,进行IP注册接口的防暴刷控制,超过阈值直接返回错误码及错误信息(错
误码:303,错误信息:您频繁进行注册,请求已被拒绝) - 添加全局过滤,进行token的验证,用户微服务和验证码微服务的请求不过滤(网关调用下游
用户微服务的token验证接口)
- Config配置中心
将数据库配置、邮件发送相关配置、防暴刷参数配置,使用Config进行管理,对应微服务可以在自己的配置文件中直接使用${xxx.yyy}的方式取出使用
1.7 关于数据表的说明
涉及到两个数据表:验证码存储表+令牌存储表,数据表参考如下
验证码存储表
-- ----------------------------
-- Table structure for dabing_auth_code
-- ----------------------------
DROP TABLE IF EXISTS `dabing_auth_code`;
CREATE TABLE `dabing_auth_code` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '⾃自增主键',
`email` varchar(64) DEFAULT NULL COMMENT '邮箱地址',
`code` varchar(6) DEFAULT NULL COMMENT '验证码',
`createtime` datetime DEFAULT NULL COMMENT '创建时间',
`expiretime` datetime DEFAULT NULL COMMENT '过期时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
令牌存储表
-- ----------------------------
-- Table structure for dabing_token
-- ----------------------------
DROP TABLE IF EXISTS `dabing_token`;
CREATE TABLE `dabing_token` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '⾃自增主键',
`email` varchar(64) NOT NULL COMMENT '邮箱地址',
`token` varchar(255) NOT NULL COMMENT '令牌',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
1.8 关于测试
- 注册测试
- 登录测试
- 未登录状态下,清空cookie,直接访问后台的邮件服务,http://www.dabing.com/api/email/{email}/{code},验证无token情况下是否被网关拦截
1.9 关于跨域
前文在 Html 静态页面中有ajax 请求数据API统一接口9002的地方,会涉及跨域问题,可以考虑将静态
资源和数据请求接口放在同一个域名下,根据url前缀在nginx层进行区分。
比如所有部署,包括nginx都在一台机器上
可以给机器设置一个域名 www.dabing.com
静态资源访问 www.dabing.com/static/xxx.html
数据API接口请求 www.dabing.com/api/xxx/yyy
通过/static和/api在nginx层进行区分
1.10 效果视频验证:
注册新账号
一分钟内只允许获取一次验证码
发邮件功能
校验验证码
验证码超时展示
保存令牌数据库
令牌保存cookie中
跳转到欢迎页面
登录
生成Token保存到令牌表和Cookies中最后转到欢迎页面
未登录状态网关拦截
IP防暴刷过滤器:在1分钟内注册超过100次时返回错误信息
二、功能实现:
2.1 一分钟内只允许获取一次验证码
2.2 IP防暴刷过滤器
@Component
@RefreshScope // 刷新配置信息
public class IpGlobalFilter implements GlobalFilter, Ordered {@Value("${filter.limit.ip.uri}")private String limitUri;@Value("${filter.limit.ip.maxtimes}")private int maxTimes;@Value("${filter.limit.ip.limitminutes}")private int limitMinutes;private static ConcurrentMap<String, List<Long>> ipCache = new ConcurrentHashMap<>();public IpGlobalFilter() {System.out.println("IpGlobalFilter初始化");}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {System.out.println(maxTimes);ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();// 客户端IPString ip = request.getRemoteAddress().getHostString();String path = request.getURI().getPath();// 如果请求的服务未设限,直接放行if (!path.startsWith(limitUri)) {return chain.filter(exchange);}// 设限服务把本次请求加入缓存List<Long> currentIpCache = ipCache.get(ip);// 初始化当前ip请求记录if (currentIpCache == null) {currentIpCache = new ArrayList<>();ipCache.put(ip, currentIpCache);}currentIpCache.add(System.currentTimeMillis());// 计算limitMinutes内访问次数是否超过maxTimesint count = 0;long startTime = System.currentTimeMillis() - (limitMinutes * 60 * 1000);for (Long reqTime : currentIpCache) {if (reqTime > startTime) {count++;}}if (count > maxTimes) {response.setStatusCode(HttpStatus.FORBIDDEN);String data = "您频繁进⾏注册,请求已被拒绝!";DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());return response.writeWith(Mono.just(wrap));}return chain.filter(exchange);}@Overridepublic int getOrder() {return 0;}
}
2.3 未登录状态网关拦截
@Component
public class TokenGlobalFilter implements GlobalFilter, Ordered {@Autowiredprivate UserFeignClient userFeignClient;/*** 进⾏token的验证,⽤户微服务和验证码微服务的请求不过滤(⽹关调⽤下游⽤户微服务的token验证接⼝)* @param exchange* @param chain* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();String path = request.getURI().getPath();// 用户微服务和验证码微服务的请求不过滤if (path.startsWith("/api/user") || path.startsWith("/api/code")) {return chain.filter(exchange);}// 获取Cookie,token不存在或者用户微服务查询不到重定向到登录页面List<HttpCookie> cookies = request.getCookies().get("token");if (!CollectionUtils.isEmpty(cookies)) {HttpCookie cookie = cookies.get(0);String token = cookie.getValue();if (!"".equals(userFeignClient.info(token))) {return chain.filter(exchange);}}// 返回状态码 303,重定向到登录页面response.getHeaders().set(HttpHeaders.LOCATION, "/static/login.html");response.setStatusCode(HttpStatus.SEE_OTHER);return response.setComplete();}@Overridepublic int getOrder() {return 0;}
}
2.4 网关配置
server:port: 9090
eureka:client:serviceUrl:defaultZone: http://dabingcloudeurekaservera:8761/eureka/,http://dabingcloudeurekaserverb:8762/eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server 可以同步注册表instance:prefer-ip-address: trueinstance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
spring:application:name: dabing-cloud-gatewaycloud:gateway:routes:- id: service-code-routeruri: lb://dabing-service-codepredicates:- Path=/api/code/**filters:- StripPrefix=1- id: service-user-routeruri: lb://dabing-service-userpredicates:- Path=/api/user/**filters:- StripPrefix=1httpclient:connect-timeout: 5000response-timeout: 20000
2.5 nginx配置
worker_processes 1;events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;upstream myServer {server 127.0.0.1:9090;}server {listen 80;server_name localhost;location /static/ {root staticDatas;}location / {root html;index index.html index.htm;}location /api {proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header REMOTE-HOST $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_pass http://myServer;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}
}
2.6 静态资源路径
注意:请求静态资源的时候,url地址栏需要使用配置的本地域名进行访问,否则报跨域问题
补充:不使用域名实现 跨域
1. 效果
2. 实现,在网关配置全局跨域
server:port: 9090
eureka:client:serviceUrl:defaultZone: http://dabingcloudeurekaservera:8761/eureka/,http://dabingcloudeurekaserverb:8762/eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server 可以同步注册表instance:prefer-ip-address: trueinstance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
spring:application:name: dabing-cloud-gatewaycloud:gateway:#开启网关的跨域功能,具体微服务上的跨域需要进行关闭,否则无效globalcors:cors-configurations:'[/**]': # 匹配所有请求allowedOrigins: "*" #跨域处理 允许所有的域allowedMethods: # 支持的方法- GET- POST- PUT- DELETEroutes:- id: service-code-routeruri: lb://dabing-service-codepredicates:- Path=/api/code/**filters:- StripPrefix=1- id: service-user-routeruri: lb://dabing-service-userpredicates:- Path=/api/user/**filters:- StripPrefix=1httpclient:connect-timeout: 5000response-timeout: 20000
仓库地址:
demo仓库地址:https://gitee.com/lg_zk/dabing-user-parent-hw.git
springcloud注册demo(使⽤第⼀代Spring Cloud核⼼组件完成项⽬构建、编码及测试)相关推荐
- SpringCloud学习笔记(1)- Spring Cloud Alibaba
文章目录 SpringCloud学习笔记(1)- Spring Cloud Alibaba 服务治理 Nacos 服务注册 Nacos 服务发现与调用 Ribbon 负载均衡 Sentinel 服务限 ...
- SpringCloud学习笔记3:Spring Cloud Netflix 组件(五大神兽)
一.Spring Cloud Netflix有哪些组件? eureka (提供服务注册与发现功能) ribbon(提供负载均衡功能) Feign(整合了ribbon和Hystrix,具有负载均衡和熔断 ...
- (十六)java springcloud版b2b2c社交电商spring cloud分布式微服务-使用spring cloud Bus刷新配置...
b2b2c电子商务社交平台源码请加企鹅求求:一零三八七七四六二六.我们使用spring cloud分布式微服务云架构做了b2b2c的电子商务系统,除了架构本身自带的系统服务外,我们将b2b2c的业务服 ...
- SpringCloud学习笔记(1)- Spring Cloud Netflix
文章目录 SpringCloud学习笔记(1)- Spring Cloud Netflix 单体应用存在的问题 Spring Cloud Eureka Eureka Server代码实现 Eureka ...
- SpringCloud学习笔记(6)----Spring Cloud Netflix之负载均衡-Ribbon的使用
1. 什么是负载均衡? 负载均衡,就是分发请求流量到不同的服务器. 负载均衡一般分为两种 1. 服务器端负载均衡(nginx) 2. 客户端负载均衡(Ribbon) 2. 服务提供者(spring-c ...
- eureka集群只注册一个_一、Spring Cloud Eureka服务注册中心
Sping boot Eureka是服务中心,管理各种服务功能包括服务的注册.发现.熔断.负载.降级等.Spring Cloud Eureka 是对Netflix公司的Eureka的二次封装. 在du ...
- Spring Cloud 各组件调优参数
Spring Cloud整合了各种组件,每个组件往往还有各种参数.本文来详细探讨Spring Cloud各组件的调优参数. Tomcat配置参数 1 server: 2 tomcat: 3 max-c ...
- Spring Cloud各组件
讲的不错:http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html Spring Cloud技术应用从场景上可以 ...
- spring cloud各组件详解
一,Eureka 服务注册中心,特性有失效剔除.服务保护.健康检查. 二.Ribbon 客户端负载均衡,特性有区域亲和.重试机制. 三.Hystrix 客户端容错保护,特性有服务降级.服务熔断.请求缓 ...
最新文章
- 如何写好一篇高质量的IEEE/ACM Transaction级别的计算机科学论文?
- tilemap 导入unity_教程|Unity中使用Tilemap快速创建2D游戏世界
- 在MFC中创建动态控件的生成与响应
- U3D中物体的渲染顺序
- 《财富》:盖茨的四项黄金法则
- mysql不能删除外键吗,MySQL不能删除外键约束所需的索引
- 平衡二叉树的自顶向下递归和自底向上递归
- c语言子线程给主线程发信息,如何用C语言实现多线程
- 心得 | 《用得上的商学课》 004 边际成本 | 飞机起飞前的座位,只卖一块钱?...
- html5中切换图片怎么做,HTML5编程实战之二:用动画的形式切换图片
- 传感器网络与物联网-1.射频识别技术
- 力扣-700 二叉搜索树中的搜索
- recordset.Open strSql, Conn, 3,3
- (原创)Lottie动画使用介绍
- 虚拟化部署ESXI6.7+intel x710-da4万兆网卡
- iPhoneXSM屏幕适配、 各机型的逻辑分辨率
- Koo叔说Shader-贴图切换
- python到底有多少个库_11个你可能不知道的Python库
- Markdown 字体颜色汇总表(简洁版)- 适用于所有需要颜色任务
- 推荐5款轻量级的小软件,界面简洁无广告