Pre和Post过滤器
通过Zuul Pre 过滤器做参数校验
/**
* 参数校验 校验过来的请求是否都带了tocken
* Created By 白
* 2019/1/2410:34
*/
@Component
public class TockienFilter extends ZuulFilter {
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext=RequestContext.getCurrentContext();
HttpServletRequest request=requestContext.getRequest();
//从url参数获取 也可以从cookie,header获取
String token=request.getParameter("token");
if (StringUtils.isEmpty(token)){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
它们各自的含义与功能总结如下:
filterType:该函数需要返回一个字符串来代表过滤器的类型,而这个类型就是在HTTP请求过程中定义的各个阶段。在Zuul中默认定义了四种不同生命周期的过滤器类型,具体如下:
pre:可以在请求被路由之前调用。
routing:在路由请求时候被调用。
post:在routing和error过滤器之后被调用,也就是请求调用后。
error:处理请求时发生错误时被调用。
filterOrder:通过int值来定义过滤器的执行顺序,数值越小优先级越高。
shouldFilter:返回一个boolean类型来判断该过滤器是否要执行。我们可以通过此方法来指定过滤器的有效范围。
run:过滤器的具体逻辑。在该函数中,我们可以实现自定义的过滤逻辑,来确定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果之后,对处理结果做一些加工等。
此时通过Zuul访问其他服务的接口必须带token否则返回401
通过Zuul Post 过滤器给返回结果的Header添加信息
/**
* 在返回的header中添加东西
* Created By 白鹏
* 2019/1/2411:00
*/
@Component
public class addResponseHeaderFilter extends ZuulFilter {
@Override
public String filterType() {
return POST_TYPE;
}
@Override
public int filterOrder() {
return SEND_RESPONSE_FILTER_ORDER -1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext=RequestContext.getCurrentContext();
HttpServletResponse response=requestContext.getResponse();
response.setHeader("X-Foo", UUID.randomUUID().toString());
return null;
}
}
限流
所有的请求都经过Zuul,所以可以通过限流来防止网络攻击
时机:请求被转发之前调用
令牌桶限流:首先根据一定速率给令牌桶放令牌,当满了就丢弃掉。当请求过来,那么先获取令牌,获取到令牌的就放行,获取不到的就拒绝。
@Component
public class RateLimitFilter extends ZuulFilter {
//创建google令牌桶限流组件,每隔一秒放100个令牌
private static final RateLimiter RATE_LIMITER=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 (!RATE_LIMITER.tryAcquire()){
throw new RateLimitException();
}
return null;
}
}
鉴权
首先在登陆方 放一个标识
卖家登陆 Cookie放入token 同时放入 redis
/**
* 卖家登陆
* @param openId
* @param response
* @return
*/
@GetMapping("/seller")
public ResultVO seller(@RequestParam("openId")String openId, HttpServletResponse response, HttpServletRequest request){
//判断是否已经登陆
Cookie cookie=CookieUtil.get(request,CookieConstant.TOKEN);
if (cookie!=null &&
StringUtils.isNotEmpty(stringRedisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_TEMPLATE.toString(),cookie.getValue())))){
return ResultVOUtil.success();
}
//查询
UserInfo info=userService.findByOpenId(openId);
//判断信息是否为空
if (info==null){
return ResultVOUtil.error(ResultEnum.LOGIN_FAIL);
}
//判断角色
if (!RoleEnom.seller.getCode().equals(info.getRole())){
return ResultVOUtil.error(ResultEnum.ROLE_ERROR);
}
//写入redis
String token= UUID.randomUUID().toString();
Integer expire = CookieConstant.expire;
stringRedisTemplate.opsForValue().set(String.format(RedisConstant.TOKEN_TEMPLATE.toString(),token),openId,expire, TimeUnit.SECONDS);
//设置Cookie
CookieUtil.set(response, CookieConstant.TOKEN,token,CookieConstant.expire);
return ResultVOUtil.success();
}
卖家对某个接口进行拦截
@Component
public class AuthSellerFilter extends ZuulFilter {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
RequestContext requestContext=RequestContext.getCurrentContext();
HttpServletRequest request=requestContext.getRequest();
if ("/order/order/finish".equals(request.getServletPath())) {
return true;
}
return false;
}
@Override
public Object run() throws ZuulException {
/**
* /order/finish 只能卖家访问 (cookie里有token)
*/
RequestContext requestContext=RequestContext.getCurrentContext();
HttpServletRequest request=requestContext.getRequest();
Cookie cookie=CookieUtil.get(request,CookieConstant.TOKEN);
if(cookie==null
|| StringUtils.isEmpty(cookie.getValue())
|| StringUtils.isEmpty(stringRedisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_TEMPLATE,cookie.getValue())))){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
跨域
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter(){
final UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();
final CorsConfiguration config=new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("*"));
config.setMaxAge(300L);
source.registerCorsConfiguration("/**",config);
return new CorsFilter(source);
}
}