このブログでは、要求者の身元を判断する承認ルールについて学びます。
1. 認可ルール
認可ルールは、要求者の身元を判断します。私を訪問する許可はありますか?そうすると、この機能は先ほどマイクロサービスを学ぶときに話したゲートウェイのようなものではないかと言われるかもしれませんが、ゲートキーパーではないでしょうか?
すべてのリクエストはゲートウェイを通過する必要があり、アイデンティティを認証してアクセス許可があるかどうかを確認します。なぜここで別のリクエストを完了する必要があるのでしょうか?
すべてのリクエストはゲートウェイのマイクロサービスを介してルーティングされますが、このとき、当然のことながら、ゲートウェイはリクエストの ID を認証できます。しかし、社内に内部関係者がいて、悪意を持った外部の人々にマイクロサービスのアドレスを漏洩した場合はどうなるでしょうか。
では、ゲートウェイをバイパスしてマイクロサービスに直接アクセスできるでしょうか? では、ゲートウェイで行うセキュリティ検証がどれほど厳格であっても、それでも役に立ちますか? マイクロサービスは他のユーザーに裸で公開されます。
したがって、Sentinel 承認ルールはリクエストの出所を確認できるため、この問題を解決できます。
ゲートウェイから来るなら逃がしてやる、別の場所から来るなら迎撃する、これで問題は解決しないだろうか?
1.1.基本ルール
Sentinel の認可ルールは比較的シンプルな構成で、主にホワイトリストとブラックリストが含まれます。
-
ホワイトリスト: 発信元がホワイトリストに含まれている発信者はアクセスを許可されます。
-
ブラックリスト: 発信元がブラックリストに含まれている発信者はアクセスを許可されません
その下に設定例を示します。
リソース名が保護されたリソース、つまりフロー制御アプリケーションであることがわかります。それがリストです。
ホワイトリストをチェックすると、許可された発信者がここに表示されます。たとえば、現在はゲートウェイからの orderService へのアクセス要求のみを許可しています。
ブラウザからお越しの場合はアクセスを禁止させていただきます。現時点では、リソース名は注文サービスで保護されているリソースです (たとえば、前の注文では {orderID})。フロー制御アプリケーションに入力されるのは、許可する発信者の名前です。ここでは、ゲートウェイがブラウザを禁止できるようにするため、ゲートウェイの名前「Geteway」を入力する必要がありますか?
いいえ、これはかなり特殊です。センチネルでは、ここに発信者の名前が表示されます。
実はそれが原点なのです。リクエストソースの名前。
1.2 原点の取得方法
では、このリクエストはどのようにして来たのでしょうか? Sentinel には RequestOriginParser と呼ばれるインターフェイスがあります。
public interface RequestOriginParser {
/**
* 从请求request对象中获取origin,获取方式自定义
*/
String parseOrigin(HttpServletRequest request);
}
リクエストオリジンリゾルバー。
parseOrigin というメソッドがあります。そのパラメータは HttpServletRequest であるため、このメソッドの機能はリクエストのリクエスト オブジェクトを取得することです。ソースの名前である、origin の値を解析する方法を見つけます。
しかし、残念ながら、デフォルトでは、sentinel メソッドの戻り結果は常にデフォルトです。
つまり。ゲートウェイ経由かブラウザ経由かは関係ありません。そのソース名はデフォルトと呼ばれます。Sentinel には、これら 2 つのリクエストを区別する方法がありません。
これをどうやって記入しますか?したがって、このインターフェイスとそのビジネス ロジックを自分で作成し、ゲートウェイからのリクエストとブラウザからのリクエストで異なる結果が返されるようにする方法を見つける必要があります。
ではソース名が違うのでしょうか?認可ルールを書くだけではだめです。しかし、このビジネス ロジックはどのように記述すればよいのでしょうか?
実際、ビジネス ロジックは、リクエスト ヘッダー、リクエスト パラメーター、Cookie のいずれであっても、区別できれば簡単に作成できます。ブラウザとゲートウェイがつながっていませんか?
たとえば、ここに例を書きました。
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. このクラスは RequestOriginParser インターフェイスを実装し、parseOrigin メソッドをオーバーライドします。
parseOriginメソッドのパラメータはHTTPリクエストを表すHttpServletRequestオブジェクトであり、リクエストヘッダのOriginフィールドを取得し、このフィールドの値をリクエスト元情報として返します。
空の場合は空白を返します。
空でない場合は、origin ヘッダーの結果をソース名として返します。
ブラウザーが取得したオリジンヘッダーがゲートウェイからのリクエストに関連している場合。
取得したオリジンヘッダーが異なる場合、ソース名も異なるのでしょうか?認可ルールを作成できるでしょうか? それで、それらは同じですか、それとも異なりますか?
実際、ゲートウェイにもブラウザにもデフォルトではこのヘッダーはありません。これは私が作ったものだからです。
1.3. ゲートウェイにリクエストヘッダーを追加する
さて、このようなヘッダーをゲートウェイからのリクエストに追加すると、ここで利用できるようになるでしょうか? それは区別ですか?したがって、このルールが何であるかは問題ではありません。将来について同意する限り、ゲートウェイにいくつかの権限を追加するだけで済みます。ここで疑問が生じます。このヘッダーをゲートウェイからのすべてのリクエストにどのように適用するかということです。
ゲートウェイについて学習していたときにフィルターについて学習したことをまだ覚えていますか?
SpringCloud のゲートウェイ サービス Gateway_gateway mono_Ajie のコード スペース ブログ - CSDN ブログ
名前はAddRequestHeaderです。
このフィルターを追加すると、ゲートウェイ経由でマイクロサービスにルーティングされるリクエストには必ずリクエスト ヘッダーが含まれるようになります。頭に関しては、そのように定義されています。
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=origin,gateway
カンマで区切ると、最初はヘッダーの名前、その後にヘッダーの値が続きます。つまり、リクエスト ヘッダーの名前はorigin、値はgatewayになります。
これで、ゲートウェイを通過するすべてのリクエストには確実にそのようなヘッダーが含まれることが明確にわかりました。その場合、そのソース名は GateWay と呼ばれる必要がありますが、ブラウザーからは必ずしもそうではありません。
1.4 認可ルールの構成
それは両者を区別するものなのでしょうか?では、承認ルールのホワイトリストに誰が記入する必要があるのでしょうか?
構成は次のとおりです。
ゲートウェイに記入する必要がありますか?
ああ、それは素晴らしいことですが、あなたの頭の中の実際の価値は何ですか? ここには何を記入する必要がありますか?
したがって、これが合意されている以上、必ずこう書かなければいけないということではありません。
もちろん、ここをどうやって記入したのですか?どのように規定されているのでしょうか?外部に公開してはならず、他人に知られた場合、リクエスト ヘッダーが偽造される可能性があります。
1.5 テスト
ここで、ゲートウェイを直接スキップして、order-service サービスにアクセスします。
ゲートウェイ経由でアクセス:
2. カスタムの異常な結果
先ほど、この認可ルールを実証しましたが、認可によってインターセプトされると、ページで例外が発生することがわかりました。
そして結果は、流量制限において例外であることが判明しました。これは問題ではありませんか? 明らかに傍受を許可していましたが、ユーザーに電流制限例外を返したため、ユーザーは混乱しました。
それは認可だけではなくて、実際、電流制限にしろ、格下げにしろ、あるいはいろいろな異常にしろ、最終的に行き着くのはこの電流制限なんです。
フレンドリーさが足りず、結果も十分に明確ではありません。
2.1 例外の種類
次に、例外をカスタマイズする方法を学びます。例外のカスタマイズは非常に簡単で、BlockExceptionHandler というインターフェイスを実装するだけです。
public interface BlockExceptionHandler {
/**
* 处理请求被限流、降级、授权拦截时抛出的异常:BlockException
*/
void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
}
例外をブロックするハンドラーです。それはなぜです?
Sentinelでは電流制限ダウングレードや認可インターセプトなどの様々な例外が発生するとBlockExceptionをスローするためです。
BlockExceptionHandler を実装している限り、この例外を処理できますが、インターフェースには handle と呼ばれるメソッドが 1 つだけあることがわかります。
このメソッドには 3 つのパラメータがあります。
- HttpServletRequest リクエスト:リクエスト对象
- HttpServletResponse 応答:応答オブジェクト
- BlockException e: センチネルによってインターセプトされたときにスローされる例外
では、この方法では、それがどのような異常であるかを判断できます。例外の種類に応じて異なる結果が返され、応答を通じてフロントエンドに書き込まれます。
では、この例外がどのタイプの例外であるかをどのように判断すればよいのでしょうか?
実際、ここでの BlockException には複数の異なるサブクラスが含まれています。
異常な | 説明する |
---|---|
フロー例外 | 電流制限例外 |
ParamFlowException | 異常なホットスポットパラメータの電流制限 |
劣化例外 | ダウングレードの例外 |
権限例外 | 認可ルールの例外 |
システムブロック例外 | システムルール例外 |
したがって、例外のタイプを判断して、これら 5 つのタイプのどれであるかを判断し、異なる結果を返し、異なる方法で処理することができます。
2.2 カスタム例外処理
次に、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 + "}");
}
}
テストを再開します。シナリオが異なると、異なる例外メッセージが返されます (フロー制御ルールの追加についてはここでは説明しません)。
3. ルールの永続化
前回の学習を経て、センチネルの一般的なゲームプレイをマスターしました。
使用する過程で、サービスを再起動するたびに、設定したさまざまなルールが失われるという問題が見つかりました。
これは、sentinel がデフォルトでこれらのルールをメモリに保存し、再起動時に失われるためです。したがって、運用環境ではそのような問題を容認することはできません。
それでは、センチネル ルールを永続化する方法を学びましょう。
3.1 ルール管理モデル
ルール管理に関しては、次の 3 つのモードがあります。
- オリジナル モード: Sentinel のデフォルト モード。ルールはメモリに保存され、サービスを再起動すると失われます。
- プルモード
- プッシュモード
オリジナルのモードはセンチネルのデフォルトモードです。このモードはセンチネルです。ルールはメモリに保存されるため、再起動すると失われます。
プル モードとプッシュ モードの両方でルールの永続化を実現できますが、実装方法に違いがあります。
3.1.1 プルモード
まずはプルモードについて説明しましょう。
Sentinel の Sentinel Dashboard コンソールの後にマイクロサービスが続きます。センチネルのクライアントです。
実際の運用では、マイクロサービスをクラスター化する必要があることを知っておく必要があります。同じマイクロサービスの複数のコピーがデプロイされますか?
ダッシュボードにルールを書き込むと、そのルールはマイクロサービスの Sentinel クライアントにプッシュされます。そして、このルールをローカル ファイルまたはデータベースに永続化するので、ルールの永続化を実現できます。
しかし、このルールを必要とする別のサービスがある場合はどうなるでしょうか? このルールが変更されたかどうかを確認するにはどうすればよいですか? したがって、マイクロサービスはこのファイルまたはデータベースを定期的にポーリングします。
データベースまたはファイルの内容の変更を聞くと、ルールが更新されたことがわかります。独自のルール キャッシュを更新できますか?
この方法では、複数の Sentinel クライアントのルールを同期できますが、この定期的なポーリング方法が使用されます。それにはいくつかの欠点があります。第一に、適時性が比較的悪いです。ここに書き込んだだけだと思います。あそこのサービスは必ずしも読んでくれるとは限らないですよね?
時間指定されているため、まだ読み取る順番が来ていない場合は、サービスの間に表示されます。データに矛盾はありませんか? ルールが矛盾しています。
したがって、このモデルには適時性の問題があり、データの不整合の問題が発生します。したがって、この解決策はあまりお勧めできません。
3.1.2 プッシュモード
それでは最後のモード、プッシュモードを見てみましょう。
プッシュ モードの Sentinel Dashboard は、このルールをどのクライアントにもプッシュしません。代わりに、このルールをリモート構成センター (前に学習した nacos など) に保存します。
これは統合された構成センターであり、Sentinel ダッシュボードはこれを nacos にプッシュします。私たちのマイクロサービスはすべて nacos を監視でき、nacos の変更を検出すると、すぐにデータを監視して更新します。
この方法では、nacos データ更新の機能を使用して、設定の更新と永続化を実現します。
したがって、それが私たちが推奨する方法です。
3.2. プッシュモードの実装
このプッシュ モードの実装は非常に複雑です。
この図は、すでに説明した、この展開モードのフローチャートです。このモードでは、Sentinel ダッシュボードがルールを Sentinel クライアントにプッシュするのではなく、nacos にプッシュする必要があることがわかります。
ただし、Sentinel のデフォルト実装では、クライアントにプッシュされます。
nacos にプッシュされたこれらの機能は、Sentinel Dashboard のソース コードには実装されていません。したがって、プッシュ モードを実装したい場合は、Sentinel Dashboard のソース コードを自分で変更する必要があります。
これは、Alibaba がかなりの泥棒であることを示すのに十分です。オープンソース フレームワークを実装しましたが、最終的にはその最良のモデルを実装しませんでした?
その後、私たちに与えられたのはデモだけで、コーディングは自分たちで変更する必要がありました。それだけでなく、侵入クライアントも nacos を監視する必要がありますか?
それで、将来的には何を変えるつもりですか?マイクロサーバー マイクロサーバーは nacos を監視します。
もちろん、変えたくないのであれば、それでいいのですが、アリババはクラウドサービスも提供しています。
このクラウドサービスは確かに有料ですが、クラウドサービスを利用すればSentinelを自分で構築する必要はありません。
そうすれば、私たちはここで彼にお金を払うことは絶対にありません、私たちはそれを自分たちで作ります。
3.2.1 order-service サービスの変更
まず、Nacos のセンチネル ルール設定をリッスンするように OrderService を変更します。
具体的な手順は次のとおりです。
1.依存関係を導入する
order-service で nacos を監視するための Sentinel の依存関係を導入します。
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2.nacosアドレスを設定する
order-service の application.yml ファイルで nacos アドレスとモニタリング構成情報を構成します。
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
さて、構成が完了したので、実際にサービスの準備が整ったので、サービスを再起動します。
3.2.2 センチネルダッシュボードのソースコードを変更する
SentinelDashboard はデフォルトでは nacos 永続性をサポートしていないため、ソース コードを変更する必要があります。
Sentinel-dashboard のソース コードは、私の公開アカウント「KnowledgeSeeker」の全員に提供されています。
これを取得するには、Sentinel ルールの永続化に返信するだけで済みます。
1.解凍する
Sentinel ソース コード パッケージを解凍し、IDEA でプロジェクトを開くと、次のような構造になっています。
2. nacos の依存関係を変更する
Sentinel-dashboard ソース コードの pom ファイルでは、nacos 依存関係のデフォルト スコープは test であり、テスト中にのみ使用できます。ここで削除する必要があります。
3.nacos サポートを追加する
Sentinel-dashboard のテスト パッケージには nacos のサポートが記述されているので、これを main にコピーする必要があります。
4.nacosアドレスを変更する
次に、テスト コード内の NacosConfig クラスも変更する必要があります。
コードは以下の通りです。
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);
}
}
nacos アドレス設定を Sentinel-dashboard の application.properties に追加します。
nacos.addr=localhost:8848
5. nacos データソースの構成
さらに、com.alibaba.csp.sentinel.dashboard.controller.v2 パッケージの下の FlowControllerV2 クラスも修復する必要があります。
追加した Nacos データ ソースを有効にします。
6. フロントエンドページを変更する
次に、フロントエンド ページを変更し、nacos をサポートするメニューを追加する必要があります。
src/main/webapp/resources/app/scripts/directives/sidebar/ ディレクトリ内のsidebar.html ファイルを変更します。
コメントのこの部分をオンにします。
その中のテキストを変更します。
7. プロジェクトを再コンパイルしてパッケージ化する
IDEA で Maven プラグインを実行して、変更された Sentinel-Dashboard をコンパイルしてパッケージ化します。
8.スタート
起動方法は公式と同じです。
java -jar sentinel-dashboard.jar
nacos アドレスを変更する場合は、パラメーターを追加する必要があります。
java -jar -Dnacos.addr=localhost:8848 sentinel-dashboard.jar
3.3 テスト
今ここには何もありません。http://localhost:8088/order/103 にアクセスしてみましょう。
その後、戻って更新してください。
ご覧のとおり、追加のフロー制御ルールがあり、これは Nacos のフロー制御ルールです。このフォームをクリックすると、ここにフロー制御ルールが追加されます。やがてナコスに入ります。
しかし、今ここに追加すると。
まだオリジナルモードなので、実際に変更したのは 1 ページだけです。理論的に言えば、実装しようとするとすべてのページを変更する必要があるため、変更は非常に大きくなります。それでは、テストしてみましょう。このフロー制御ルールを見てください。
次に、[フロー制御ルールの追加] を再度クリックします。
フロー制御ルールを /order/{orderId} に追加します。
これをフロー制御ルール - NACOS に追加する必要があります。他の場所では引き続き元のモードを使用する必要があります。
NACOSに行ってリフレッシュしましょう。
追加の構成があることがわかりました。
ブラウザにアクセスして猛烈な勢いで更新し、現在の制限ルールがあるかどうかを確認します。
後でサービスを再起動して、構成が失われるかどうかを確認します。