4. 授权规则
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情
授权规则可以对请求方来源做判断和控制。很多时候我们需要根据调用方来判断该次请求是否放行,这时候可以使用 Sentinel 的来源访问控制功能。
1. 授权规则
1.1 基本规则
-
授权规则可以通过对来源方做控制,有白名单和黑名单两种方式
- 若配置白名单,则只有请求来源位于白名单内时才可通过;
- 若配置黑名单,则请求来源位于黑名单时不允许通过,其余的请求来源可以通过。
-
资源名:就是受保护的资源,例如/order/{orderId}
-
流控应用:是来源者的名单
- 如果是勾选白名单,则名单中的来源被许可访问。
- 如果是勾选黑名单,则名单中的来源被禁止访问。
-
工作流程
-
在网关配置文件中配置请求头,将请求头作为标识信息。可以看作是一个另类的 token 我们这里就设置了一个 origin 请求头并传入了 gateway 的值。
-
Sentinel 在获取请求并作出识别之前,需要做两件事情。
- 第一件事情是配置授权规则,指定标识信息。我们通过 Sentinel 控制台就可以完成
-
第二件事是将获取的请求获取请求头,看看里面的内容是否符合要求。Sentinel 是通过 RequestOriginParser 来获取和处理标识信息。
-
-
请求全过程
走网关的请求因为带有标识信息,所以能够成功访问到 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 配置授权规则
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 + "}"); } }重启服务后重测: