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-providercloud:nacos:discovery:server-addr: 10.211.55.11:8848,10.211.55.12:8848,10.211.55.13:8848enabled: truesentinel:transport:dashboard: 127.0.0.1:9000eager: trueweb-context-unify: falsedatasource:authority-nacos-datasource:nacos:server-addr: 10.211.55.11:8848,10.211.55.12:8848,10.211.55.13:8848group-id: DEFAULT_GROUPnamespace: publicdata-id: ${spring.application.name}-sentinel-authoritydata-type: jsonrule-type: authorityusername: nacospassword: nacos
然后注册一个 RequestOriginParser 类型的 Bean。
@Component
public class MyRequestOriginParser implements RequestOriginParser {@Overridepublic 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> {@Overridepublic 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);}@Overridepublic 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;
}