[MSA]認証システム開発者(3)-API間の通信と構成サーバ


1.API間の通信


前回の記事では、gatewayとeurekaを介してmsaサービスを構築する方法について説明しました.
でも開発が終わってから問題が発生しました
ドメインごとにサーバを分割してユーザデータを表示するには、Authサーバが重複するEntityを作成し、DBにアクセスする必要があります.このやり方はmsaの原則に違反するだけでなく,重複するコードを増加させる.したがって、Authサーバは、ログインしようとするユーザの情報を受信するために、ユーザサーバに要求を送信しなければならない.
従って、マイクロサービスは、機能を実行するために適切なデータをユーザに配信するために、各マイクロサービスインスタンスが有機的にインタラクションする方法を採用する.
サーバ間の通信方法は、主に次のとおりです.
同期:Rest Template、Feign Client(HTTP)/g Rpc
非同期:Kafka、Amazon SQL(AMPプロトコル)などのMessage Agent
本プロジェクトでは、Spring Cloud Feign ClientというREST apiを使用します.

1.1. Feign Client接続


Authサーバへの依存性を追加し、@EnableFeignClientsの操作説明を追加します.
build.gradle
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
UserApplication.java
@EnableFeignClients
@SpringBootApplication
public class UserApplication {
	public static void main(String[] args) {
		SpringApplication.run(UserApplication.class, args);
	}
}

1.2. インタフェースの作成とサービスコール


Http Endpointからデータを受信するインタフェースを作成します.Eurekaに登録されているInstance名をFeignClientオペレーティングシステムに追加すると、自動的にマッピングされます.
UserFeignClient.java
@FeignClient(name = "AUTH-USER-SERVER")
public interface UserFeignClient {
    @PostMapping("/email")
    UserResponseDto getUser(@RequestParam String email);
}
サービスがクライアントを呼び出すと、要求がユーザー・サーバ上の対応するエンドポイントに送信され、データが受信されます.
AuthService.java
@Service
@RequiredArgsConstructor
public class AuthenticationService {
    ...
    private final UserServiceClient userServiceClient;

    @Transactional
    public TokenDto loginUsers(AuthenticationRequestDto dto) {

        UserResponseDto user = userServiceClient.getUser(dto.getEmail());

        String salt = user.getSalt();
        String password = saltUtil.encodePassword(salt,dto.getPassword());
        
        if(!user.getPassword().equals(password)){
            throw new BadRequestException(ExceptionType.INCORRECT_PASSWORD);
        }

        TokenDto tokenDto = jwtTokenProvider.generateToken(user);
        redisUtil.setDataExpire(user.getName(),tokenDto.getRefreshToken(), 60 * 30L);
        return tokenDto;
    }
}

2.サーバー配置の構成


2.1. Spring Cloud Config


各マイクロサービスには、上記のJWTトークンまたはDataBase設定のような繰り返しの設定があります.
Spring Cloud Configサーバは、これらのシステムの優先パラメータを集中させ、変更をリアルタイムで適用できます.したがって、変更を行う場合は、毎日수정-빌드-재배포の負担を負う必要はありません.
また、パスワードや秘密鍵などの機密情報がサービス項目から露出しないため、セキュリティ面でも改善される.
コンフィギュレーションサーバで使用される設定ファイルを管理するには、ローカルファイル、Gitリポジトリなど、さまざまな方法がありますが、Spring ConfigではGitの使用を推奨しているため、この方法を採用しています.

2.2. Git Repositoryの作成


まずgithubにアクセスしてリモートレジストリを作成し、環境固有の設定ファイルを作成します.
yamlファイルの名前は${ApplicationName}-${EnvironmentName}.ymlに設定する必要があります.
この名前に基づいて、http://CONFIG-SERVER/{ApplipcationName}/{EnvironmentName}に後でアクセスします.
# application.yml (필수X)
default:
  owner: config-service's git folder
  content: properties for msa project

# user-service.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/ncns_db
    username: username
    password: password
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
        use_sql_comments: true
        format_sql: true
        show_sql: true
    hibernate:
      ddl-auto: update
    generate-ddl: true
token:
  key: tokenkey
default:
  message: user-service default properties


# user-service-test.yml
spring:
  datasource:
    url: jdbc:h2:mem:testdb
    username: sa
    password:
    driverClassName: org.h2.Driver
  jpa:
    show_sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.H2Dialect
token:
  key: tokenkey
default:
  message: user-service test profiles

2.3. Spring Cloud Configプロジェクトの作成


スプリングアイテムを作成して、次の依存項目と@EnableConfigServerアクションを追加します.
build.gradle
implementation 'org.springframework.cloud:spring-cloud-config-server'
ConfigApplication.java
@EnableConfigServer
@SpringBootApplication
public class ConfigApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConfigApplication.class, args);
	}
}
configサーバのapplication.ymlファイルに、ポートなどのプリファレンスとサービスごとの設定情報を格納するリポジトリアドレスを入力します.
レジストリがプライベートである場合は、ユーザー名、パスワード、Tokenを追加する必要があります.(メッセージエラーがない場合、リクエスト時に不正なエラーが発生します)
application.yml
server:
  port: 9020
spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        git:
          uri: https://github.com/sgs-ncns/NCNS-Config
          username: {username}
          password: {password}
サーバを実行し、http://localhost:9020/user-サービス/テスト GETリクエストを送信することで、テストの優先パラメータ情報を正常に読み込むことができます.

2.4. Config Client登録


コンフィグ・サーバから設定を受信し、次の依存性をユーザー・サーバ(ユーザー・サーバなど)に追加します.
build.gradle
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
bootstrap.ymlファイルを生成し、config serverのuriを指定します.必要に応じてprofileを指定して使用します.bootstrap.ymlで作成されたコンテンツは、application.ymlより先にロードされます.したがって、アプリケーションを実行する前に、以前に設定したconfigサーバからプロファイルをロードして実行する必要があります.
bootstrap.yml
spring:
  cloud:
    config:
      uri: http://localhost:9020
      profile: test
コントローラに次のEndPointを追加して、設定情報が正しく入力されていることを確認できます.
UserController.java
public class UserController {
    ...
    @GetMapping("/config")
    public String config(@Value("${spring.datasource.url}") String url,
                           @Value("${spring.datasource.username}") String username,
                           @Value("${spring.datasource.password}") String password,
                           @Value("${token.key}") String tokenKey) {
        return "url: " + url + "\n"
                + "username: " + username + "\n"
                + "password: " + password + "\n\n"
                + "token key: " + tokenKey;
    
    }
    ...
}

3.暗号化構成サーバー設定情報


ただしprivate repositoryにアップロードしても、パスワードやsecret keyは純粋なテキストにアップロードすべきではありません.
そこでSpring Cloud Configの暗号化/復号化とJasyptを用いて情報を暗号化する.

3.1. Spring Cloud Configの暗号化/復号化


次の依存項目をConfigサーバに追加して起動します.ymlを有効にします.
build.gradle
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
ブートファイルを作成し、暗号化キーを入力します.
bootstrap.yml
encrypt:
  key: supersecretencryptkey
次のエンドポイントで暗号化する情報をテキストとしてマスターに送信すると、暗号化文字列が返されます.
暗号化:http://config-server.com/encrypt POST
復号化:http://config-server.com/decrypt POST
この文字列を{ciper}encrypted-string形式でコンフィギュレーションライブラリに挿入すると適用されます.
# user-service.yml
spring:
 datasource:
   url: jdbc:postgresql://localhost:5432/ncns_db
   username: "{cipher}76ccc5039cd999c9635904f3be4d0f9f7ecbc94445bf283e9df6c9d345d28d0f"
   password: "{cipher}a2ae0947fa5e86871198868db6ce2a660440b9fd37b5488ba85471e4b4f4f5d5"
 jpa:
   properties:
     hibernate:
       dialect: org.hibernate.dialect.PostgreSQLDialect
       use_sql_comments: true
       format_sql: true
       show_sql: true
   hibernate:
     ddl-auto: update
   generate-ddl: true
token:
 key: "{cipher}0edc3d96596eca424ca60bcdf73f05dd82abc7a6d6763a65d744df1e768e3d15"
default:
 message: user-service default profiles properties

3.2. Jasypt暗号化


しかし、bootstrap.ymlは依然として暗号鍵を暴露している.これは、誰もが元のテキストを復号して読むことができることを意味します.
したがって、この鍵もJasyptを使用して暗号化されます.実際、Spring Cloud Configを暗号化/復号する必要はなく、このJasyptを使用するだけですべて暗号化できます.
Jasyptを使用する場合は、次の依存項目を追加し、jasypt.encryptor.passwordを設定する必要があります.
build.gradle
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.4'
Encryptorを詳細に設定するために個別に構成を作成できますが、DefaultLazyEncryptorはプロジェクト環境変数にパスワードを登録するだけで正常に動作します.
@Configuration
public class JasyptConfig {

    @Bean("jasyptStringEncryptor")
    public StringEncryptor stringEncryptor() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        config.setPassword("password");
        config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
        config.setKeyObtentionIterations("1000");
        config.setPoolSize("1");
        config.setProviderName("SunJCE");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
        config.setStringOutputType("base64");
        encryptor.setConfig(config);
        return encryptor;
    }
}
テストコードを作成し、簡単な暗号化を行うことができます.
Jasyptは複数の暗号化プログラムをサポートしているので、適切な暗号化プログラムを選択できます.
JasyptEncryptTest.java
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class EncryptTest {

    @Value("${jasypt.encryptor.password}")
    private String encryptorPassword;

    private String target;

    @Autowired
    private ConfigurableEnvironment configurableEnvironment;

    @BeforeEach
    private void beforeTest() {
        target = "plain-text";
    }

    @Test
    public void standardPBEStringEncryptorTest() {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword(encryptorPassword);
        System.out.println("StandardPBEStringEncryptor : " + encryptor.encrypt(target));
    }

    @Test
    public void defaultLazyEncryptorTest() {
        DefaultLazyEncryptor encryptor = new DefaultLazyEncryptor(configurableEnvironment);
        System.out.println("DefaultLazyEncryptor : " + encryptor.encrypt(target));
    }

    @Test
    public void pooledPBEStringEncryptorTest() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        encryptor.setPoolSize(1);
        encryptor.setPassword(encryptorPassword);
        System.out.println("PooledPBEStringEncryptor : " + encryptor.encrypt(target));
    }

}
暗号化文字列はENC(encrypted-string)形式に変更されます.
bootstrap.yml
encrypt:
  key: ENC(7gkouBcaWqwM6iEDwLkmibJnTWyvmx4p3uwJqMS5wm5W8feVM54Emfi6WANVjmWm)

🚩 EOD


これで、簡単にMSAプロジェクトを組織する文章は終わりました!
修正や改善が必要な点も多いですが、今回のプロジェクトではMSAを使用する理由とメリットとデメリットを知りました.👏
また、Gatewayから暗号化ツールまで、これらはすべて多くの選択肢があり、それぞれが比較され、プロジェクトに必要なものを分析し、適切な技術を採用するのは面白いプロセスです.
読むだけで堂々としたプロジェクトができる文章をまとめて、これからこの文章を見てくれる人の役に立つことを願っています.🥰

Ref.


OpenFeign vs Rest Template
How should I choose between gRPC and Kafka when building microservices?
マイクロサービス、どのように実施しますか?
マイクロサービスアーキテクチャIPC
マイクロサービスアーキテクチャの通信
https://daddyprogrammer.org/post/4347/spring-cloud-msa-configuration-server/
Spring Cloud Configアプリケーションの設定情報(アプリケーション.yml)を一元管理
[SC 03]Spring Cloud Configとは?
Jasypt暗号化
Spring Boot設定ファイルの暗号化(アプリケーション.yml)