1. 在zuul网关服务中实现限流
1.1 为什么需要限流?
限流是为了保证服务器的负载量处于正常状态,因为如果太多的访问量可能会直接导致服务的崩溃。
1.2 如何实现限流
1.2.1 令牌桶算法简介
在zuul中实现限流是通过创建一个filter实现的,底层算法采用的是令牌桶算法,令牌桶会每秒往桶中投放一定数量的令牌,如果令牌桶中的令牌达到了临界值,新产生的令牌就会丢弃,当请求到达zuul网关后,会向令牌桶中提取令牌,如果成功拿到令牌则放行请求,如果没有拿到令牌则直接拒绝。
1.2.2 编码实现限流过滤器
package com.qingyun.apigetaway.filter;
import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import com.qingyun.apigetaway.exception.RateLimterException;
import org.springframework.stereotype.Component;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVLET_DETECTION_FILTER_ORDER;
@Component
public class RateLimiterFilter extends ZuulFilter {
//限流桶实现 每秒钟100个令牌
private static final RateLimiter RATELIMITER=RateLimiter.create(100);
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
//限流过滤器要最先运行
return SERVLET_DETECTION_FILTER_ORDER-1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
if(!RATELIMITER.tryAcquire()){//判断是否取到令牌
//没有取到令牌 直接抛出异常
throw new RateLimterException();
}
return null;
}
}
这里需要注意的是,限流过滤器要放在其他所有过滤器之前。
2. 在zuul网关服务中实现用户鉴权(访问鉴权)
2.1 为什么需要鉴权操作?
鉴权操作在每一个项目中都是必须要具备的一个功能,因为在每一个项目中都会有敏感数据,一些比较重要的数据有具有相关权限的用户才能够访问,比如在一个点餐项目中,普通用户只能够访问商品的信息,而不能对商品信息进行修改,而对于卖家(管理员),它即可访问商品信息,也可以对商品信息进行修改。
2.2 如何实现访问鉴权?
2.2.1 首先搭建SpringCloudBus、StringCloudConfigClient用于动态更新访问路径,如果尚未搭建zuul可以查看我的另一篇博客:使用zuul构建ApiGetaway网关服务
2.2.1.1 导入配置客户端、事件消息总线、redis所需的maven依赖
<dependencies>
<!--
省略部分maven依赖
-->
<!--SpringCloudConfig配置客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--SpingCloudBus事件消息总线 用户动态更新配置信息-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--用于验证用户信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
2.2.1.2 编写相关配置 bootstrap.yml
server:
port: 8001
spring:
application:
name: apiGetaway
cloud:
config:
discovery:
enabled: true #开启配置中心发现
service-id: config-server #配置中心的名称
profile: dev
bus:
id: ${spring.application.name}:${spring.cloud.config.profile}:${random.value}
profiles:
active: dev
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
2.2.2 编写路径授权配置类
package com.qingyun.apigetaway.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-02-18
* Time: 21:43
* Explain: 路径授权配置类
*/
@Component
@ConfigurationProperties(prefix = "auth-request")
@RefreshScope
@Data
public class AuthPathConfig {
private List<String> buyerPaths;
private List<String> sellerPaths;
private List<String> commonPaths;
}
2.2.3 在git远程仓库中添加相关配置信息
auth-request:
buyerPaths: #买家可访问的路径 ${order.server.name}是一个spel表达式用于获取order服务的名称
- /${order.server.name}/order/create
- /myOrder/order/create
sellerPaths: #卖家可访问的路径
- /${order.server.name}/order/finish
- /myOrder/order/finish
commonPaths: #公共路径
- /${product.server.name}/product/list
- /myProduct/product/list
2.2.4 编写买家访问授权过滤器、卖家访问授权过滤器
我们首先约定cookie中带有openid的为买家,cookie中带有token的为卖家。
- 买家访问授权过滤器
package com.qingyun.apigetaway.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.qingyun.apigetaway.config.AuthPathConfig;
import com.qingyun.apigetaway.utils.CookieUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-02-18
* Time: 20:12
* Explain: 买家鉴权过滤器
*/
@Component
public class AuthBuyerFilter extends ZuulFilter {
@Autowired
private AuthPathConfig authPathConfig;
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER-1;
}
@Override
public boolean shouldFilter() {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String uri=request.getRequestURI();
if(authPathConfig.getBuyerPaths().contains(uri)||authPathConfig.getCommonPaths().contains(uri)){
return true;
}
return false;
}
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String openid = CookieUtil.getValue("openid");
if(StringUtils.isBlank(openid)){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
- 卖家访问授权过滤器,在这里还需要通过token查询redis进行身份认证
package com.qingyun.apigetaway.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.qingyun.apigetaway.config.AuthPathConfig;
import com.qingyun.apigetaway.constants.TokenConstant;
import com.qingyun.apigetaway.utils.CookieUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-02-18
* Time: 20:12
* Explain: 买家鉴权过滤器
*/
@Component
public class AuthSellerFilter extends ZuulFilter {
@Autowired
private AuthPathConfig authPathConfig;
@Autowired
private RedisTemplate redisTemplate;
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER-1;
}
@Override
public boolean shouldFilter() {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String uri=request.getRequestURI();
if(authPathConfig.getSellerPaths().contains(uri)||authPathConfig.getCommonPaths().contains(uri)){
return true;
}
return false;
}
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String token = CookieUtil.getValue(TokenConstant.TOKEN_KEY);
if(StringUtils.isEmpty(token)||
StringUtils.isEmpty(redisTemplate.opsForValue().get(String.format(TokenConstant.TOKEN_TEMPLATE,token)).toString())){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
以上代码编写完毕后就是可以实现访问路径控制了,授权的路径也可以实现动态更新。
3. zuul实现跨域访问
3.1 为什么需要实现跨域访问?
因为浏览器的同源策略的原因,会导致与当前访问的域名不一致的资源无法正常访问,要想对这些不同源的资源访问,那么必须做特殊处理。
3.2 实现跨域访问的方式有哪些?
前端使用Jsonp,还可以通过配置代理服务器的方式访问,也可以通过zuul服务网关对请求进行处理。
3.3 使用zuul实现跨域访问的具体步骤
3.3.1 编写一个跨域配置类
package com.qingyun.apigetaway.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-02-19
* Time: 11:51
* Explain: zuul跨域访问配置类
* C-Cross O-Origin R-Resource S-Sharing
*/
@Configuration
public class CorsConfig {
public CorsFilter corsFilter(){
final UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();
CorsConfiguration config=new CorsConfiguration();
config.setAllowCredentials(true); //允许跨域cookie
config.setAllowedOrigins(Arrays.asList("*")); //设置原始域名
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("*"));
config.setMaxAge(300L);
source.registerCorsConfiguration("/**",config);
return new CorsFilter(source);
}
}
配置完成后就可以实现跨域资源访问了,其他跨域实现方法还可以参考我的另一篇博文:SpringBoot实现跨域资源访问