[技术陈旧] SpringCloud-Zuul网关 自定义ZuulFilter过滤器实现登录鉴权

过滤器类型和请求的生命周期

Zuul 中的过滤器跟我们之前使用的 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可以通过配置 urlPatterns 来拦截对应的请求。

而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。

1)pre
可以在请求被路由之前调用。适用于身份认证的场景,认证通过后再继续执行下面的流程。

2)route
在路由请求时被调用。适用于灰度发布场景,在将要路由的时候可以做一些自定义的逻辑。

3)post
在 route 和 error 过滤器之后被调用。这种过滤器将请求路由到达具体的服务之后执行。适用于需要添加响应头,记录响应日志等应用场景。

4)error
处理请求时发生错误时被调用。在执行过程中发送错误时会进入 error 过滤器,可以用来统一记录错误信息。

 


        通过上面的图可以清楚地知道整个执行的顺序,请求发过来首先到 pre 过滤器,再到 routing 过滤器,最后到 post 过滤器,任何一个过滤器有异常都会进入 error 过滤器。

        通过 com.netflix.zuul.http.ZuulServlet 也可以看出完整执行顺序,ZuulServlet 类似 Spring-Mvc 的 DispatcherServlet,所有的 Request 都要经过 ZuulServlet 的处理。



自定义过滤器需要继承 ZuulFilter,并且需要实现下面几个方法:

1)shouldFilter
是否执行该过滤器,true 为执行,false 为不执行,这个也可以利用配置中心来实现,达到动态的开启和关闭过滤器。

2)filterType
过滤器类型,可选值有 pre、route、post、error。

3)filterOrder
过滤器的执行顺序,数值越小,优先级越高。

4)run
执行自己的业务逻辑,本段代码中是通过判断请求中是否有token,决定是否进行拦截。当token为空时,通过设置 requestContext.setSendZuulResponse(false),告诉 Zuul 不需要将当前请求转发到后端的服务了。通过 setResponseBody 返回数据给客户端。

 



通过上面的铺垫了解,下面正试开始用Zuul网关ZuulFilter过滤器实现登录鉴权
Zuul 网关服务项目的环境搭建,
可参考博文 
[技术陈旧] SpringCloud-Zuul网关项目 环境搭建和整合Eureka访问_springcloud创建一个zuul网关服务,实现通过网关访问eureka client中的接口-CSDN博客
项目核心代码,Zuul过滤器代码

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
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_TYPE;

/**
 * 登录过滤器
 */
//这个注解不要忘了,加入spring容器中
@Component
public class LoginFilter  extends ZuulFilter {

    /**
     * 过滤器类型
     * org.springframework.cloud.netflix.zuul.filters.support.FilterConstants 这个类
     * pre:在请求被路由(转发)之前调用
     * route:在路由(请求)转发时被调用
     * error:服务网关发生异常时被调用
     * post:在路由(转发)请求后调用
     * @return
     */
    @Override
    public String filterType() {
        //前置过滤器
        return PRE_TYPE;
    }

    /**
     * 过滤器顺序,越小越先执行
     * @return
     */
    @Override
    public int filterOrder() {

        return 4;
    }


    /**
     * 过滤器是否生效
     * @return
     */
    @Override
    public boolean shouldFilter() {

        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        System.out.println(request.getRequestURI()); ///apigateway/product/api/v1/product/list
        System.out.println(request.getRequestURL()); //http://localhost:9000/apigateway/product/api/v1/product/list

        //ACL
        //进行拦截,就会进入下面的 run方法中
        if (request.getRequestURI().contains("/apigateway/orderapi/ordermapping/savedingdan")){
            return true;
        }

        //不拦截,放行
        return false;
    }

    /**
     * 业务逻辑
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {

        //获取上下文
        RequestContext requestContext =  RequestContext.getCurrentContext();
        //获取request对象
        HttpServletRequest  request = requestContext.getRequest();

        //token对象
        String token = request.getHeader("token");

        if(StringUtils.isBlank((token))){
            token  = request.getParameter("token");
        }

        //登录校验逻辑  根据公司情况自定义 JWT
        //token为空,就不能访问
        if (StringUtils.isBlank(token)) {
            //停止访问,并返回出错的消息
            requestContext.setSendZuulResponse(false);
            
            //防止中文乱码
            requestContext.getResponse().setContentType("text/html;charset=UTF-8");
            //设置返回的状态码和正文
            requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            requestContext.setResponseBody("userToken is null");
        }
        //正常的话,继续向下走
        return null;
    }
}

效果验证
1 当访问包含"/apigateway/orderapi/ordermapping/savedingdan"的请求时,就会拦截,如果有token,就可以正常访问,没有token,就会终止访问
2 其它请求不拦截,可正常访问

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值