Sentinel架构篇 - 来源访问控制

来源访问控制(黑白名单)

概念

Sentinel 提供了黑白名单限制资源能否通过的功能。如果配置了白名单,则只有位于白名单的请求来源的对应的请求才能通过;如果配置了黑名单,则位于黑名单的请求来源对应的请求不能通过。

实际操作

在这里插入图片描述

在 Nacos 控制台的配置管理/配置列表中,在 public 的命名空间中创建一个如下配置:

dataId:spring-cloud-demo-provider-sentinel-authority

group:DEFAULT_GROUP

配置内容如下:

[
    {
    
    
        "resource": "/hello",
        "limitApp": "white-origin",
        "strategy": 0
    },
    {
    
    
        "resource": "/hello/say",
        "limitApp": "black-origin",
        "strategy": 1
    }
]

strategy 为 0 表示白名单;为 1 表示黑名单。

limitApp 如果有多个请求来源,则使用逗号分隔。


对应的客户端的配置文件如下:

spring:
    application:
        name: spring-cloud-demo-provider
    cloud:
        nacos:
            discovery:
                server-addr: 10.211.55.11:8848,10.211.55.12:8848,10.211.55.13:8848
                enabled: true
        sentinel:
            transport:
                dashboard: 127.0.0.1:9000
            eager: true
            web-context-unify: false
            datasource:
                authority-nacos-datasource:
                    nacos:
                        server-addr: 10.211.55.11:8848,10.211.55.12:8848,10.211.55.13:8848
                        group-id: DEFAULT_GROUP
                        namespace: public
                        data-id: ${
    
    spring.application.name}-sentinel-authority
                        data-type: json
                        rule-type: authority
                        username: nacos
                        password: nacos

然后注册一个 RequestOriginParser 类型的 Bean。

@Component
public class MyRequestOriginParser implements RequestOriginParser {
    
    
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
    
    
        String origin = httpServletRequest.getHeader("origin");
        if (StringUtils.isBlank(origin)) {
    
    
            origin = "default";
        }
        return origin;
    }
}

请求时,在请求头添加一对 origin, whiteOrigin 或者 origin, blackOrigin 就能看到黑白名单对于特定资源的限制效果了。

实际操作中,个人发现如果被黑白名单限制的话,程序不会抛出异常,而是响应中输出 Blocked by Sentinel (flow limiting)。

AuthoritySlot

负责对请求来源的授权规则进行校验。

@Spi(order = Constants.ORDER_AUTHORITY_SLOT)
public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> {
    
    

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
        throws Throwable {
    
    
      	// 校验请求来源的授权规则
        checkBlackWhiteAuthority(resourceWrapper, context);
      	// 交给下一个ProcessorSlot继续处理
        fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }

    @Override
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
    
    
      	// 交给下一个ProcessorSlot继续处理
        fireExit(context, resourceWrapper, count, args);
    }

    void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
    
    
      	// 加载所有资源的授权规则
        Map<String, Set<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();
		// 如果授权规则列表为空,则直接返回
        if (authorityRules == null) {
    
    
            return;
        }

      	// 获取指定资源的授权规则列表
        Set<AuthorityRule> rules = authorityRules.get(resource.getName());
      	// 如果对应的授权规则列表为空,则直接返回
        if (rules == null) {
    
    
            return;
        }
      
		// 遍历授权规则列表
        for (AuthorityRule rule : rules) {
    
    
          	// 如果有一个授权规则没有校验通过,则抛出异常
            if (!AuthorityRuleChecker.passCheck(rule, context)) {
    
    
                throw new AuthorityException(context.getOrigin(), rule);
            }
        }
    }
}

接下来看下 AuthorityRuleChecker 的 passCheck 方法的内部逻辑。

static boolean passCheck(AuthorityRule rule, Context context) {
    
    
  	// 获取上下文记录的请求来源
    String requester = context.getOrigin();

    // 如果请求来源为空或者授权规则的limitApp为空,则返回true,表示校验通过
    if (StringUtil.isEmpty(requester) || StringUtil.isEmpty(rule.getLimitApp())) {
    
    
        return true;
    }

    // 判断授权规则的limitApp中是否存在指定的请求来源
    int pos = rule.getLimitApp().indexOf(requester);
    boolean contain = pos > -1;

    if (contain) {
    
    
        boolean exactlyMatch = false;
      	// 用逗号分隔
        String[] appArray = rule.getLimitApp().split(",");
      	// 判断授权规则中是否对指定的请求来源进行了限制
        for (String app : appArray) {
    
    
            if (requester.equals(app)) {
    
    
                exactlyMatch = true;
                break;
            }
        }

        contain = exactlyMatch;
    }

    int strategy = rule.getStrategy();
  	// 如果设置了黑名单,并且授权规则中包含指定的请求来源,则返回false,表示校验不通过
    if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {
    
    
        return false;
    }
	// 如果设置了白名单,并且授权规则中不包含指定的请求来源,则返回false,表示校验不通过
    if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) {
    
    
        return false;
    }
	// 其余请求,返回true,表示校验通过
    return true;
}

猜你喜欢

转载自blog.csdn.net/qq_34561892/article/details/129410134