Sentinel authorization rules&& rule persistence

In this blog, we will learn about authorization rules. Authorization rules are a judgment on the identity of the requester.

1. Authorization rules

Authorization rules make a judgment on the identity of the requester. Do you have permission to visit me? Then some people may say that this function is like the gateway we mentioned before when we were learning about microservices. Isn’t it the gatekeeper?

All requests must go through the gateway, which authenticates the identity to see if you have permission to access it. Why do I need to complete another one here?

All requests are routed through the microservices of the gateway. At this time, the gateway can of course authenticate the identity of the request. But what if there is an insider in your company and he leaks the address of your microservices to those with malicious intentions outside.

So guys, can they bypass the gateway and access microservices directly? So no matter how strict the security verification you do in your gateway is, is it still useful? Your microservices are nakedly exposed to others.

Therefore, our Sentinel authorization rules can solve this problem because it can verify where your request comes from.

If you are coming from the gateway, I will let you go. If you are coming from somewhere else, I will intercept you. Wouldn’t this solve the problem?

1.1.Basic rules

In Sentinel's authorization rules, the configuration is relatively simple, mainly including whitelist and blacklist.

  • Whitelist: Callers whose origin is in the whitelist are allowed to access

  • Blacklist: Callers whose origin is in the blacklist are not allowed to access

Below, is an example of configuration.

image-20230319112047997

You can see that the resource name is the protected resource, the flow control application. That’s the list.

If you check whitelist, here are the permitted callers. For example, I now only allow requests from the gateway to access orderService.

image-20230319112216316

If you come here from a browser, I will prohibit your access. At this time, the resource name is the protected resource in the order service, for example, {orderID} in our previous order. What is filled in the flow control application is the name of the caller you allow. So here we allow the gateway to ban the browser, so do we need to fill in the name of the gateway, Geteway?

No, this is quite special. In sentinel, here is the name of the caller.

Actually it is origin. The name of your request source.

1.2 How to obtain origin

So, how did this request come from? There is an interface in our Sentinel called RequestOriginParser.

public interface RequestOriginParser {
    
    
    /**
     * 从请求request对象中获取origin,获取方式自定义
     */
    String parseOrigin(HttpServletRequest request);
}

Request origin resolver.

There is a method in it called parseOrigin. Its parameter is HttpServletRequest, so the function of this method is to obtain the request object of your request. Find a way to parse out the value of origin, which is the name of the source.

But unfortunately, by default, the return result of the sentinel method is always default.

That is to say. It doesn't matter whether you come through the gateway or the browser. Its source name is called default. Sentinel has no way to distinguish between these two requests.

How do you fill this in? Therefore, we must find a way to write this interface and its business logic ourselves, and then let requests from the gateway and requests from the browser return different results.

So their source names are different? If we don't, we can write authorization rules. But how should this business logic be written?

In fact, business logic is easy to write, whether it is from request headers, request parameters, or cookies, as long as you can distinguish them. Wouldn’t a browser and a gateway suffice?

For example, I wrote an example here.

package com.jie.order.sentinel;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * 通过解析请求头中的Origin字段,获取请求来源信息
 */
@Component
public class HeaderOriginParser implements RequestOriginParser {
    
    

    /**
     * 解析请求头中的Origin字段,获取请求来源信息
     * @param request HTTP请求
     * @return 请求来源信息
     */
    @Override
    public String parseOrigin(HttpServletRequest request) {
    
    
        // 1.获取请求头中的Origin字段
        String origin = request.getHeader("origin");
        // 2.如果请求头中的Origin字段为空,则设置默认值为"blank"
        if (StringUtils.isEmpty(origin)) {
    
    
            origin = "blank";
        }
        // 3.返回请求来源信息
        return origin;
    }
}

The name is HeaderOriginParser. This class implements the RequestOriginParser interface and overrides the parseOrigin method.

The parameter of the parseOrigin method is an HttpServletRequest object, which represents an HTTP request. It will obtain the Origin field in the request header and return the value of this field as the request source information.

If it is empty, I will return blank.

If it is not empty, I will return the result of the origin header as the source name.

If the origin header obtained by the browser is related to the request from the gateway.

If the obtained origin headers are different, then are their source names different? Can I write authorization rules? So what are the same and different between them?

In fact, neither the gateway nor the browser has this header by default, because I made it up.

1.3. Add a request header to the gateway

Now, if I add such a header to the request coming from the gateway, will it be available here? Is that a distinction? So it doesn't matter what this rule is, as long as you agree on the future, we can add some privileges to the gateway. So here comes the question, how do we add this header to all the requests from the gateway?

Do you still remember that you learned a filter when you were learning about gateways?

SpringCloud's Gateway Service Gateway_gateway mono_Ajie's Code Space Blog-CSDN Blog

The name is AddRequestHeader.

Then, after adding this filter, any request routed to the microservice through the gateway will inevitably bring a request header. Then this head is defined like this.

image-20230319121029996

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=origin,gateway

Separated by commas, the first is the name of the header, followed by the value of the header. So, the name of my request header is origin, and the value is gateway.

Now we clearly know that all requests that go through the gateway will definitely carry such a header. Then its source name must be called gateWay, but it is not necessarily the case from the browser, right?

1.4 Configure authorization rules

Does this separate the two? So who should fill in the whitelist in our authorization rules?

image-20210716153250134

The configuration is as follows:

image-20210716153301069

Do you need to fill in the gateway?

Oh, that's great, so what's the actual value in your head? What do you need to fill in here?

Therefore, as long as this is agreed upon, it does not mean that it must be written this way.

Of course, how did you fill it out here? How is it stipulated? It must not be exposed to outsiders. If others know it, they can forge the request header.

1.5 Testing

Now, we skip the gateway directly and access the order-service service:

image-20230319120545761

Access via gateway:

image-20230319121228273

2. Customize abnormal results

Just now, we have demonstrated this authorization rule, then we found that when we were authorized to intercept, what we got on the page was an exception.

image-20230319120545761

And the result turned out to be an abnormal flow limiting current limit. Isn’t this a problem? You are clearly authorized to intercept, and you return a flow-limiting exception to others, and the user is stunned.

And it's not just authorization. In fact, whether it's the current limit we're talking about, whether it's a downgrade, or all kinds of abnormalities, it's the current limit that we get in the end.

Not friendly enough and the results are not clear enough.

2.1 Exception types

Then we will learn about it next, how to customize exceptions? Custom exceptions are very simple, you only need to implement an interface called BlockExceptionHandler.

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

It is a handler that blocks exceptions. Why is it?

Because when various exceptions such as current limit downgrade, authorization interception, etc. occur in Sentinel, it will throw BlockException.

As long as you implement BlockExceptionHandler, then you can handle this exception. You see, there is only one method in the interface called handle.

This method has three parameters:

  • HttpServletRequest request:request对象
  • HttpServletResponse response:response对象
  • BlockException e: the exception thrown when intercepted by sentinel

Then you are in this method, you can judge which kind of exception it is, and then what? Different results are returned according to the type of exception, and written to the front end through response.

So how do I judge what type of exception this exception is?

In fact, the BlockException here contains multiple different subclasses:

abnormal illustrate
FlowException Current limiting exception
ParamFlowException Abnormal hotspot parameter current limit
DegradeException Downgrade exception
AuthorityException Authorization rule exception
SystemBlockException System rule exception

So we can make a judgment on the exception type to see which of these five types it is, so as to return different results and handle it differently.

2.2 Custom exception handling

Next, we define a custom exception handling class in order-service:

package com.jie.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;

/**
 * Sentinel的异常处理器,用于处理Sentinel的流量控制、熔断降级和授权管理等异常
 */
@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
    
    

    /**
     * 处理Sentinel的异常
     *
     * @param request  HTTP请求
     * @param response HTTP响应
     * @param e        Sentinel的异常
     * @throws Exception 抛出异常
     */
    @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;
        }

        // 设置HTTP响应的内容类型和状态码,并输出响应内容
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(status);
        response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
    }
}

Restart the test. In different scenarios, different exception messages will be returned. (I will not demonstrate adding flow control rules here)

image-20230319131603524

image-20230319131742038

3. Rule persistence

After the previous study, we have mastered the common gameplay of sentinel.

In the process of using it, we found a problem, that is, every time our service is restarted, the various rules we configured are lost.

This is because sentinel will save these rules in memory by default and will be lost upon restart. Then we definitely cannot tolerate such a problem in a production environment.

So let’s learn how to persist sentinel rules.

3.1 Rule management model

As for rule management, it has three modes:

  • Original mode: Sentinel's default mode, the rules are saved in memory and will be lost when the service is restarted.
  • pull mode
  • push mode

The original mode is the default mode of sentinel. This mode is sentinel. The rules will be saved in memory, so they will be lost upon restart.

Both pull and push modes can achieve the persistence of rules, but there are differences in the implementation methods.

3.1.1 pull mode

Let's first talk about the pull mode first.

image-20230319132407747

Sentinel's Sentinel Dashboard console is followed by a microservice. It is a client of Sentinel.

You have to know that in actual production, microservices must be clustered. Will multiple copies of the same microservice be deployed?

When you write a rule into a Dashboard, the rule will be pushed to a Sentinel client of the microservice. And it will persist this rule to a local file or database, so that we can achieve the persistence of the rule.

But what if I have another service that also needs this rule? How do I know if this rule has changed? Therefore, our microservices will poll this file or database regularly.

When I hear changes in the contents of the database or file, I will know that the rules have been updated. Can I update my own rule cache?

In this way, the rules of multiple Sentinel clients can be synchronized, but this method of regular polling is used. It has some shortcomings. First, it is relatively poor in timeliness. You think you just wrote it in here. That service over there may not necessarily read it, right?

It is timed, so if it has not yet had its turn to read, then it will be between your services. Is the data inconsistent? The rules are inconsistent.

Therefore, this model has a timeliness problem, which leads to a data inconsistency problem. Therefore, this solution is not very recommended.

3.1.2 push mode

Then let's take a look at the last mode, push mode.

image-20230319134318519

Push mode Sentinel Dashboard will not push this rule to any client. Instead, save this rule to a remote configuration center, such as the nacos we learned before.

This is a unified configuration center, and Sentinel Dashboard pushes this thing to nacos. Our microservices can all monitor nacos. Once they discover changes in nacos, they will immediately monitor and update the data.

Then if their local rules have changed accordingly, they will naturally take effect. This method uses the feature of nacos data update to achieve an update and persistence of the configuration.

So it is the way we recommend.

3.2. Implement push mode

This push mode is quite complicated to implement.

image-20230319134318519

This picture is what we have already talked about, the flow chart of this deployment mode. Then we know that in this mode, Sentinel Dashboard needs to push the rules to nacos, instead of pushing them to the Sentinel client.

However, in the default implementation of Sentinel, it is pushed to the client.

These functions pushed to nacos are not implemented in the source code of Sentinel Dashboard. So if we want to implement push mode, we have to change the source code of Sentinel Dashboard ourselves.

This is enough to show that Alibaba is quite a thief. Did you implement an open source framework, but in the end you did not implement its best model?

Then all we were given were demos, and we had to change the coding ourselves. Not only that, but does our penetration client also need to monitor nacos?

So what are you going to change in the future? The microservice side needs to monitor nacos.

So there are still a lot of things to change. Of course, if you don’t want to change, that’s fine. Alibaba also provides a cloud service.

This cloud service definitely charges money. If you use its cloud service, you don't need to build Sentinel yourself.

Then we will definitely not pay for him here, we will build it ourselves.

3.2.1 Modify the order-service service

First modify the OrderService so that it listens to the sentinel rule configuration in Nacos.

Specific steps are as follows:

1.Introduce dependencies

Introduce the dependency of sentinel to monitor nacos in order-service:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

2. Configure nacos address

Configure the nacos address and monitoring configuration information in the application.yml file in order-service:

spring:
  cloud:
    sentinel:
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 # nacos地址
            dataId: orderservice-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow # 还可以是:degrade、authority、param-flow

Ok, after configuration here, our service is actually ready, then we can restart the service.

3.2.2 Modify sentinel-dashboard source code

SentinelDashboard does not support nacos persistence by default, and the source code needs to be modified.

The sentinel-dashboard source code is provided to everyone in my public account "KnowledgeSeeker".

image-20230319135902957

You only need to reply to the Sentinel rule persistence to get it.

1. Unzip

Unzip the sentinel source package, and then open the project with IDEA, the structure is as follows:

image-20230319145947177

2. Modify nacos dependencies

In the pom file of the sentinel-dashboard source code, the default scope of nacos dependency is test, which can only be used during testing. It needs to be removed here:

image-20230319151915648

3. Add nacos support

Under the test package of sentinel-dashboard, support for nacos has been written, and we need to copy it to main.

image-20230319151005628

4. Modify nacos address

Then, you also need to modify the NacosConfig class in the test code:

image-20230319151058973

The code is placed below.

package com.alibaba.csp.sentinel.dashboard.rule.nacos;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

/**
 * @author Eric Zhao
 * @since 1.4.0
 */
@Configuration
@ConfigurationProperties(prefix = "nacos")
public class NacosConfig {
    
    
    
    /**
     * nacos 地址
     */
    private String addr;

    public String getAddr() {
    
    
        return addr;
    }

    public void setAddr(String addr) {
    
    
        this.addr = addr;
    }


    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
    
    
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
    
    
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public ConfigService nacosConfigService() throws Exception {
    
    
        return ConfigFactory.createConfigService(addr);
    }
}

Add nacos address configuration in application.properties of sentinel-dashboard:

nacos.addr=localhost:8848

image-20230319152433969

5. Configure nacos data source

In addition, you also need to repair the FlowControllerV2 class under the com.alibaba.csp.sentinel.dashboard.controller.v2 package:

image-20230319152530917

Let the Nacos data source we added take effect:

image-20230319153202564

6. Modify the front-end page

Next, we need to modify the front-end page and add a menu that supports nacos.

Modify the sidebar.html file in the src/main/webapp/resources/app/scripts/directives/sidebar/ directory:

image-20230319153441514

Turn on this part of the comment:

image-20230319153533469

Modify the text inside:

image-20230319153659541

7. Recompile and package the project

Run the maven plug-in in IDEA to compile and package the modified Sentinel-Dashboard:

image-20230319153834008

8. Start

The startup method is the same as the official one:

java -jar sentinel-dashboard.jar

If you want to modify the nacos address, you need to add parameters:

java -jar -Dnacos.addr=localhost:8848 sentinel-dashboard.jar

3.3 Testing

image-20230319154307139

There is nothing here now. Let's visit http://localhost:8088/order/103

image-20230319154413762

Then go back and refresh it.

image-20230319154431218

As you can see, there is an additional flow control rule now, which is the flow control rule of Nacos. If you click on this form, the flow control rule will be added here. Eventually you will enter Nacos.

But, if you add it here now.

image-20230319154818340

It is still in original mode, so actually I only changed one page here. Theoretically speaking, if you want to implement it, you have to change every one of them, so the change is very big. Now, let’s test it. Take a look at this flow control rule.

Then let’s click on Add Flow Control Rule.

image-20230319155006460

We add a flow control rule to that /order/{orderId}.

image-20230319155204685

We must add it to our flow control rule-NACOS, and use the original mode in other places.

Let’s go to NACOS and refresh it.

image-20230319155301601

Found that there is an extra configuration.

I will go to the browser to refresh frantically to see if there are any current limiting rules.

image-20230319155343244

Let's restart the service now to see if our configuration will be lost.

Guess you like

Origin blog.csdn.net/weixin_53041251/article/details/129651977