Sentinel:授权规则

468 阅读3分钟

4. 授权规则

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情

授权规则可以对请求方来源做判断和控制。很多时候我们需要根据调用方来判断该次请求是否放行,这时候可以使用 Sentinel 的来源访问控制功能。

1. 授权规则

1.1 基本规则

  • 授权规则可以通过对来源方做控制,有白名单和黑名单两种方式

    • 若配置白名单,则只有请求来源位于白名单内时才可通过;
    • 若配置黑名单,则请求来源位于黑名单时不允许通过,其余的请求来源可以通过。

    image-20220827222057364

    • 资源名:就是受保护的资源,例如/order/{orderId}

    • 流控应用:是来源者的名单

      • 如果是勾选白名单,则名单中的来源被许可访问。
      • 如果是勾选黑名单,则名单中的来源被禁止访问。
  • 工作流程

    1. 在网关配置文件中配置请求头,将请求头作为标识信息。可以看作是一个另类的 token 我们这里就设置了一个 origin 请求头并传入了 gateway 的值。

    2. Sentinel 在获取请求并作出识别之前,需要做两件事情。

      1. 第一件事情是配置授权规则,指定标识信息。我们通过 Sentinel 控制台就可以完成
    3. 第二件事是将获取的请求获取请求头,看看里面的内容是否符合要求。Sentinel 是通过 RequestOriginParser 来获取和处理标识信息。

  • 请求全过程

    image-20220827221940969

    走网关的请求因为带有标识信息,所以能够成功访问到 order 服务器。而浏览器直接发送请求则被拒绝。

1.2 如何获取请求头

Sentinel 是通过 RequestOriginParser 来获取和处理标识信息。

  package cn.itcast.order.sentinel;

  import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
  import io.netty.util.internal.StringUtil;
  import org.apache.commons.lang.StringUtils;
  import org.springframework.stereotype.Component;

  import javax.servlet.http.HttpServletRequest;

  /**
  * @author HGD
  * @date 2022/8/27 20:40
  */
  @Component
  public class HeaderOriginParser implements RequestOriginParser {
      @Override
      public String parseOrigin(HttpServletRequest httpServletRequest) {
          // 1. 获取 origin 请求头的内容作为标识信息。
          String origin = httpServletRequest.getHeader("origin");
          // 2. 非空判断 若为空设置请求头为blank
          if (StringUtils.isEmpty(origin)) {
              origin = "blank";
          }
          // 否则直接返回
          return origin;
      }
  }

1.3 网关添加请求头

修改网关配置文件

spring:
  cloud:
    gateway:
      routes:
      	......
      default-filters:
        - AddRequestHeader=指定请求头,请求头内容
        - AddRequestHeader=origin,gateway

1.4 配置授权规则

image-20220827230826222

2. 自定义异常结果

默认情况下,触发流控限流、熔断、热点限流、授权拦截的时候,都会抛出异常到调用方,返回的异常界面都是一样的。这样子不好区分是因为那种规则导致的异常。所以需要自定义异常返回信息,明确是出发了哪种类型的规则。

2.1 异常类型

  • 异常类型

    异常说明
    FlowException限流异常
    ParamFlowException热点参数限流的异常
    DegradeException降级异常
    AuthorityException授权规则异常
    SystemBlockException系统规则异常

2.2 自定义异常处理

  • 实现接口

    我们是通过实现 BlockExceptionHandler 接口来自定义异常是的返回结果:

    public interface BlockExceptionHandler {
        /**
         * 处理请求被限流、降级、授权拦截时抛出的异常:BlockException
         */
        void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
    }
    
    • HttpServletRequest request:request对象
    • HttpServletResponse response:response对象
    • BlockException e:被sentinel拦截时抛出的异常
  • 具体案例

    可以将这个自定义异常处理方法做成自定义异常处理类,这样子就可以全局通用了。

    package cn.itcast.order.sentinel;
    
    import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
    import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
    import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
    import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @Component
    public class SentinelExceptionHandler implements BlockExceptionHandler {
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
            // 异常信息和状态码
            String msg = "未知异常";
            int status = 429;
    
            // 根据不同的异常返回不同的状态信息和状态码
            if (e instanceof FlowException) {
                msg = "请求被限流了";
            } else if (e instanceof ParamFlowException) {
                msg = "请求被热点参数限流";
            } else if (e instanceof DegradeException) {
                msg = "请求被降级了";
            } else if (e instanceof AuthorityException) {
                msg = "没有权限访问";
                status = 401;
            }
    
            // 设置响应类型
            response.setContentType("application/json;charset=utf-8");
            // 设置状态码
            response.setStatus(status);
            // 设置异常信息
            response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
        }
    }
    

    重启服务后重测:

    image-20220827233728490

    image-20220827233752121