- 投稿日:2022-03-18T22:44:59+09:00
ibatisでThere is no WRITEABLE property named ''xx" in class xxのエラー
最近ずっとStrutsでiBatisを使ったプログラムの改修をしていて、このエラーに遭遇したのですが、地味にググってもちょっと解決に詰まったのでメモ書き。 エラーメッセージは以下のような形式で出ていました。 com.ibatis.common.beans.ProbeException: There is no WRITEABLE property named 'xxx列名' in class 'パッケージ名.クラス名' で、原因はというとほぼこのブログで書かれていた内容ではあったのですが地味に違っていて、なんとカラム名にアンダースコアが入っているときはgetterとsetterにも同様にアンスコを入れないと認識されない、でした。 例えば今回の場合、取得したいDBテーブルのカラム名がprev_ansidsで取得用のDtoでは以下のような変数名にセットするようにしていた場合、以下のようなsetterとgetter名にするとこのエラーが出ます: AnswerDto.java private String prev_ansIds; public String getPrevAnsIds() { return prev_ansIds; } public void setPrevAnsIds(String prev_ansIds) { this.prev_ansIds = prev_ansIds; } これをちゃんと以下のようなコードに直したらエラーは出なくなりました。 AnswerDto.java private String prev_ansIds; public String getPrev_ansIds() { return prev_ansIds; } public void setPrev_ansIds(String prev_ansIds) { this.prev_ansIds = prev_ansIds; }
- 投稿日:2022-03-18T21:15:11+09:00
java初心者の日記(1)
前まではRuby だったり Pythonでプログラミングを学んでいたのですが内定を頂いた会社の研修でjavaを使用するとのことなのでjavaを学ぼうと思います。3日坊主なのでどこまで続くかわかりませんが、、 使用する教材は「スッキリわかるjava入門」 数日かけて12章まで進んだのでオブジェクト指向についてのメモを残します。 オブジェクト指向とはソフトウェアを開発するときの部品化のこと だそうです。今まで学んできて少しは理解した気がしますがオブジェクト指向とは何か、という風に説明することは難しそうです。 教科書と同じような作品を自分なりに作ってみました。めちゃくちゃ簡単ですがやはり自分で作った方が理解につながりますね。 Main.java public class Main{ public static void main(String[] args){ //名前の入力 System.out.println("名前を入力してください"); //勇者の誕生 String name = new java.util.Scanner(System.in).nextLine(); Hero h = new Hero(name); System.out.println("勇者"+name+"が誕生した"); System.out.println("町から外に出た"); System.out.println("少し歩く"); //魔物の誕生 System.out.println("魔物が現れた"); Matango m = new Matango(); //戦闘 System.out.println("戦う:1 , 逃げる:2"); int input = new java.util.Scanner(System.in).nextInt(); if (input == 1){ while(h.hp>0 && m.hp>0){ //勇者の攻撃 h.attack(m); if(m.hp<=0){ break; } //魔物の攻撃 m.attack(h); if(h.hp<=0){ break; } } }else if (input == 2){ h.run(); }else{ System.out.println("1か2を選んでください"); } } } Hero.java public class Hero{ String name; int level; int hp; int offence; int defence; public Hero(String name){ this.name = name; this.level = new java.util.Random().nextInt(5)+1; switch(this.level){ case 1: System.out.println("レベルは1です"); this.hp = 10; this.offence = 10; this.defence = 10; break; case 2: System.out.println("レベルは2です"); this.hp = 10; this.offence = 20; this.defence = 20; break; case 3: System.out.println("レベルは3です"); this.hp = 10; this.offence = 30; this.defence = 30; break; case 4: System.out.println("レベルは4です"); this.hp = 10; this.offence = 40; this.defence = 40; break; case 5: System.out.println("レベルは5です"); this.hp = 10; this.offence = 50; this.defence = 50; break; } } public void attack(Matango m){ System.out.println("勇者の攻撃"); m.hp-=this.offence; if(m.hp <=0){ m.hp = 0; finish(); } } public void run(){ System.out.println("勇者は逃げ出した"); } public void finish(){ System.out.println("勇者は勝利した"); } } Matango.java public class Matango{ int hp; int offence; public Matango(){ System.out.println("お化けキノコだ"); this.hp = 10; this.offence = 3; } public void attack(Hero h){ System.out.println("お化けキノコの攻撃"); h.hp-=this.offence; if(h.hp <= 0){ h.hp = 0; finish(); } } public void finish(){ System.out.println("勇者は負けた"); } } めちゃくちゃ改善点だらけですが、、、まあ、、、、動けばいいんです!! 少しずつ改良したいと思いますが使っている本だとコマンドラインでのアプリしか作れないので自分なりに頑張ってやるしかないですね
- 投稿日:2022-03-18T16:47:41+09:00
Scalaの勉強はじめました 1 〜基本的な文法と素因数分解〜
はじめに N予備校のプログラミング入門コースでNode.jsを学び、その勢いのままScalaの勉強を同じくN予備校で受講し始めました。 用語 JVM Javaを動かすための、Java Virtual Machine (JVM)という仮想マシン sbt Scalaで書いたコードをコンパイルしてビルドしてくれるツール sbt console ScalaのREPLが表示される。 sbt run REPLに入らずにプログラムの実行と終了を行う。 文法 Main.scala object Main extends App { println("Hello Scala") } Mainというオブジェクトを宣言。 Appというトレイト(振る舞い)をミックスイン(振る舞いをオブジェクトに対して追加)する。 名前からするに、機能の継承というか拡張という感じ。 Appというのは基底クラスに当たるんだろうか。 println: プリントライン コンソールに出力して、「改行」する。 変数 val cat = "猫" valは定数の宣言。 varもあるけれど、JSのletみたいなものはなさそう。 関数 def cat(name: String): String = {} defで宣言して、String型を返す。 C#のvoidや.NETのSubプロシージャにあたるものがUnitかなと。 素因数分解するプログラム import scala.math.sqrt object Factorization extends App { // 素因数分解する値 val target = 510510 // 割る数の最大値は 対象となる値の平方根 val maxDivisor = sqrt(target).toInt def factorizationRec(num: Int, divisor: Int, acc: Map[Int,Int]): Map[Int, Int] = { // 最大値を超えたら if (divisor > maxDivisor) { // 限界まで(1まで)割れたらその結果を返す // 限界まで割れなかった場合、配列に (現在の値, 1) を追加する if (num == 1) acc else acc + (num -> 1) } else if (num % divisor == 0) { // 現在の配列に (現在の割る数, 次に割る数)を追加する // 現在の割る数が、現在の配列になかった場合、次に割る数を1として配列に追加する val nextAcc = acc + (divisor -> (acc.getOrElse(divisor, 0) + 1)) factorizationRec(num / divisor, divisor, nextAcc) } else { factorizationRec(num, divisor + 1, acc) } } println(factorizationRec(target, 2, Map())) } 実際にどうなるかを書き出してやっと理解できた。 とにかく2で割って、割り切れなくなったら3で割って・・・を繰り返す処理。最終的には何で何回割ったかを配列で返す。 再帰呼び出しは理解に時間がかかります。 その分理解できるとスッキリするけれど。 まとめ 新しく言語を勉強すると、まず文法に慣れるところから始まります。 すでに基礎編は半分終わってるんですが、少しずつまとめていきたいと思います。
- 投稿日:2022-03-18T15:35:14+09:00
Bean Validationで日付のフォーマット+存在チェックを行うアノテーションを作成する
はじめに Spring BootでREST API開発を行っている際に、パラメータの入力値が正しい日付であるか確認が必要になってきた。 nullチェックや最大値チェックなどは、チェックできるアノテーションがあらかじめ用意されているが、日付に関しては用意されていなかったためチェック方法を検討することに。 はじめは@Patternでチェックできると思ったが、存在しない日付はチェックできなかったり、毎回正規表現を記載する必要があるため、他の方法を調査。 すると、独自のアノテーションを作成できることがわかったためその方法で実現してみることにした。 アノテーションでやりたいこと nullの場合はチェックを行わない フォーマット(yyyy/MM/dd HH:mm:ss)に該当するかのチェックを行う 存在する日付かどうかチェックを行う フォーマット、存在する日付のチェックでそれぞれメッセージを変更する カスタムアノテーションを作成 Hibernate Validatorのリファレンスを参考にカスタムアノテーションを作成する。 依存関係 以下の依存関係が必要です。 Spring Bootの場合はもとから含まれているはずなので設定不要です。 必要があれば追加してください。 pom.xml <dependencies> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>7.0.4.Final</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>jakarta.el</artifactId> <version>4.0.1</version> </dependency> </dependencies> アノテーションの実装 今回作成するアノテーション名は@TimeStampとする。 TimeStamp.java import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import jakarta.validation.Constraint; import jakarta.validation.Payload; @Target({ ElementType.FIELD, ElementType.PARAMETER }) //アノテーションの使用できる箇所。今回はフィールドとパラメータのみ。必要であれば随時追加を。 @Retention(RetentionPolicy.RUNTIME) // 実行時に利用可能にする。 @Constraint(validatedBy = TimeStampValidator.class) // 検証を行うバリデーターを記載。後に記載。 @Documented @Repeatable(TimeStamp.List.class) // 同じ場所で複数回使用できるようにするために必要。 public @interface TimeStamp { String message() default "デフォルトメッセージ"; // バリデーションのデフォルトでエラーメッセージ。ないと怒られる。 String formatUnMatchMessage() default "yyyy/MM/dd HH:mm:ss形式で設定してください。"; // フォーマットチェック時のエラーメッセージ。独自に追加。 String inCorrectDateMessage() default "存在する日付を指定してください。"; // 存在日付チェック時のエラーメッセージ。独自に追加。 Class<?>[] groups() default {}; // アノテーションのグループ化に使用する。適用する優先順位を付けたいときなど。ないと怒られる。 Class<? extends Payload>[] payload() default {}; // 同じアノテーションを割り当てたときの重要度の変更に用いる。詳しくはレファレンス参照。ないと怒られる。 @Target({ ElementType.FIELD, ElementType.PARAMETER }) // @Repeatable(TimeStamp.List.class)を付与している際に必要。 @Retention(RetentionPolicy.RUNTIME) @Documented @interface List { TimeStamp[] value(); } } バリデーターの実装 TimeStampValidator.java import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.format.ResolverStyle; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; public class TimeStampValidator implements ConstraintValidator<TimeStamp, String> { private String formatUnMatchMessage; private String inCorrectDateMessage; // 初期化時にアノテーションクラスからエラーメッセージを取得 @Override public void initialize(TimeStamp timeStamp) { this.formatUnMatchMessage = timeStamp.formatUnMatchMessage(); this.inCorrectDateMessage = timeStamp.inCorrectDateMessage(); } // 実際に検証を行うメソッド @Override public boolean isValid(String value, ConstraintValidatorContext context) { // valueに検証対象の値が格納される。 // nullの場合はチェックしない if (value == null) { return true; } // 日付フォーマットチェック if (value.matches( "^[0-9]{4}/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])\\s([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$") == false) { changeErrorMessage(context, formatUnMatchMessage); return false; } // 日付存在チェック try { LocalDate.parse(value, DateTimeFormatter.ofPattern("uuuu/MM/dd HH:mm:ss").withResolverStyle(ResolverStyle.STRICT)); } catch (DateTimeParseException e) { changeErrorMessage(context, inCorrectDateMessage); return false; } return true; } // エラーメッセージの変更を行う void changeErrorMessage(ConstraintValidatorContext context, String message) { context.disableDefaultConstraintViolation(); // デフォルトのConstraintViolationを無効にする。 context.buildConstraintViolationWithTemplate(message) // テンプレートにより設定したいエラーメッセージでConstraintViolationを作成。 .addConstraintViolation(); } } 参考文献をもとにJava8以降のため、日付存在チェックではDateTimeFormatterを使用。 このとき、ResolverStyle.STRICTを設定しないと、4/31 を 5/1 としてよしなにやってしまうので注意。 アノテーションを使用するクラスの実装 以下のようにフィールドに対して設定します。 AnnotationDemo.java public class AnnotationDemo { @TimeStamp private String date; public AnnotationDemo(String date) { this.date = date; } public String getDate() { return this.date; } } アノテーションを付与するフィールドごとにエラーメッセージを変更したい場合は、以下のように設定します。 AnnotationDemo.java public class AnnotationDemo { @TimeStamp(formatUnMatchMessage = "フォーマットチェックカスタムメッセージ", inCorrectDateMessage = "存在チェックカスタムメッセージ") private String date; public AnnotationDemo(String date) { this.date = date; } public String getDate() { return this.date; } } 検証 テストクラス 以下のようにアノテーションが有効か確認しました。一部を記載します。 class AnnotationDemoTest { @Test @DisplayName("dateが2022/01/01 11:11:11のとき、バリエーション結果は存在しない") void dateが存在する日付のときバリエーション結果は存在しない() { // 準備 ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); // 実行 boolean isValidateResult = validator .validateValue(AnnotationDemo.class, "date", "2022/01/01 11:11:11") .iterator() .hasNext(); // 検証 assertEquals(false, isValidateResult); } // 以下その他のテストメソッド } テスト結果から問題なく動作していることが確認できました。 ソース こちらで公開しています。 おわりに Bean Validationを使ったアノテーション実装にはなりますが、実際にやってみるとやる前に比べてだいぶ心理的障壁がなくなりました。 何事も挑戦してみるのは大事ですね。 参考文献 Hibernate Validator 7.0.4.Final - Jakarta Bean Validation Reference Implementation: Reference Guide Javaにおける日付文字列の書式チェック方法 日付および時刻の正規表現
- 投稿日:2022-03-18T11:01:29+09:00
DefaultAzureCredential では環境変数を使うと便利です(Java on Azure)
はじめに Azure で Java アプリケーションを開発していたとき、認証方式の設計で悩む機会がありました。 本記事では、Java アプリケーションで実装可能な認証方式である DefaultAzureCredential を使った認証について、実開発でつまづきがちなポイントをまとめます。 この記事に記載の内容は、あくまで私個人の見解であり、所属する会社&組織の見解を必ずしも反映したものではありません。ご了承ください。 DefaultAzureCredential DefaultAzureCredential とは、開発環境・Azure デプロイ後それぞれで使用される資格情報を組み合わせて使用することができる認証方式です。 DefaultAzureCredential は、アプリケーションが最終的に Azure クラウドで実行されるほとんどのシナリオに適しています。 DefaultAzureCredential は、デプロイ時の認証に一般的に使用される資格情報と、開発環境での認証に使用される資格情報を組み合わせたものです。 引用: Azure でホストされる Java アプリケーションを認証する Docs にも記載の通り、DefaultAzureCredential を使用すると、既定では、以下の順序で認証を実施します。 順序 認証方式 1 環境変数 2 マネージド ID 3 IntelliJ アカウント 4 Visual Studio Code 5 Azure CLI 環境 - DefaultAzureCredential は、DefaultAzureCredentialで指定されたアカウント情報を読み取り、それを使用して認証を行います。 マネージド ID - マネージド ID が有効になっている Azure ホストにアプリケーションがデプロイされている場合、DefaultAzureCredential はそのアカウントを使用して認証を行います。 IntelliJ - Azure Toolkit for IntelliJ 経由で認証した場合、DefaultAzureCredential はそのアカウントを使用して認証を行います。 Visual Studio Code - Visual Studio Code Azure Account プラグインを使用して認証した場合、DefaultAzureCredential はそのアカウントを使用して認証を行います。 Azure CLI - Azure CLI の az login コマンドを使用してアカウントを認証した場合、DefaultAzureCredential はそのアカウントを使用して認証を行います。 引用: 既定の Azure 資格情報 環境変数を使った認証フロー DefaultAzureCredential を認証方式として採用すると、環境変数を使った認証が最優先で実行されます。 環境変数を使った認証を実行するには、以下の 3 つの環境変数が全てセットされている必要があります。 変数名 値 AZURE_CLIENT_ID Azure AD アプリケーション ID AZURE_CLIENT_SECRET Azure AD アプリケーションに設定されたクライアントシークレット AZURE_TENANT_ID Azure AD テナント ID 個人的な見解ですが、認証フローに関して、特別な要件がない限り、環境変数を使って認証を実行することをおすすめします。 理由は、ローカル開発時とクラウドへのデプロイ後とで、同様の方式で認証を実施することが可能だからです。詳細は後述します。 サンプルプログラム DefaultAzureCredential を使って認証し、Key Vault に格納されているシークレットを取得するサンプルプログラムを書きました。 今回作成したプログラムは、以下に配置しています。 Gitリポジトリ: 『kohei3110/JavaOnAzureAuthDemo』 開発はローカルマシンにて行い、Web Apps にアプリケーションをデプロイします。 サービスプリンシパルの作成 まず、Azure 上のリソースにアクセスするために、開発において使用する資格情報を作成する必要があります。ここで使用する資格情報は、なるべく人に依存させず、アプリケーションそのものに資格情報を設定したいですよね。 なぜなら、仮に開発者の方の資格情報を使って認証していた場合、その開発者が退職してしまったらどうでしょう。その開発者のユーザーが削除された場合、認証そのものがその時点で通らなくなってしまうなどのケースが考えられますよね。 こういうケースに対応するため、Azure ではサービスプリンシパルという機能が用意されています。 今回も、新たにサービスプリンシパルを作成しました。サービスプリンシパルの作成方法については、以下 Docs をご確認ください。 Docs: 『リソースにアクセスできる Azure AD アプリケーションとサービス プリンシパルをポータルで作成する』 環境変数の設定 サービスプリンシパルが作成されると、アプリケーションID、クライアントシークレットが払い出されます。DefaultAzureCredential で環境変数を用いた認証を行う際には、これらを環境変数としてあらかじめ設定しておく必要があります。 今回は、以下のようにシェルスクリプトを用いて環境変数をセットしました。 env.sh export AZURE_CLIENT_ID=<Azure AD アプリケーション ID> export AZURE_CLIENT_SECRET=<Azure AD アプリケーションに設定されたクライアントシークレット> export AZURE_TENANT_ID=<Azure AD テナント ID> サンプルプログラムの作成 今回は、上記の資格情報を用いて、あらかじめ Key Vault に格納されたシークレットを読み込むプログラムを書きました。 サンプルプログラムは下記の通りです。 GetSecret.java package com.koheisaito.javaonazureauthdemo; import java.util.logging.Logger; import com.azure.identity.DefaultAzureCredential; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.security.keyvault.secrets.SecretClient; import com.azure.security.keyvault.secrets.SecretClientBuilder; import com.azure.security.keyvault.secrets.models.KeyVaultSecret; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class GetSecret { private static final String SECRET_NAME = "storageaccountconnectionstring"; private static final String keyVaultName = "key-b1f4eaa7158ef33d"; Logger logger = Logger.getLogger(GetSecretTest.class.getName()); private String keyVaultUri = "https://" + keyVaultName + ".vault.azure.net"; @GetMapping("/secrets") public void getSecret() { DefaultAzureCredential defaultAzureCredential = buildCredential(); SecretClient secretClient = buildSecretClient(keyVaultUri, defaultAzureCredential); KeyVaultSecret keyVaultSecret = secretClient.getSecret(SECRET_NAME); String secret = keyVaultSecret.getValue(); logger.info("Secret: " + secret); } public DefaultAzureCredential buildCredential() { DefaultAzureCredential defaultAzureCredential = new DefaultAzureCredentialBuilder().build(); return defaultAzureCredential; } public SecretClient buildSecretClient(String vaultUrl, DefaultAzureCredential credential) { SecretClient client = new SecretClientBuilder() .vaultUrl(vaultUrl) .credential(credential) .buildClient(); return client; } } 上記コードを実行すると、以下のように出力されます。認証が成功し、Key Vault シークレットが取得できていることがわかります。 2022-03-18 10:49:59.629 INFO 125200 --- [nio-8080-exec-1] c.azure.identity.EnvironmentCredential : Azure Identity => EnvironmentCredential invoking ClientSecretCredential 2022-03-18 10:49:59.697 ERROR 125200 --- [nio-8080-exec-1] c.a.i.i.IntelliJCacheAccessor : IntelliJ Authentication not available. Please log in with Azure Tools for IntelliJ plugin in the IDE. 2022-03-18 10:49:59.883 INFO 125200 --- [nio-8080-exec-1] c.a.s.k.secrets.SecretAsyncClient : Retrieving secret - storageaccountconnectionstring 2022-03-18 10:50:22.704 INFO 125200 --- [onPool-worker-3] c.azure.identity.ClientSecretCredential : Azure Identity => getToken() result for scopes [https://vault.azure.net/.default]: SUCCESS 2022-03-18 10:50:22.719 INFO 125200 --- [onPool-worker-3] c.azure.identity.DefaultAzureCredential : Azure Identity => Attempted credential EnvironmentCredential returns a token 2022-03-18 10:50:33.378 INFO 125200 --- [ctor-http-nio-5] c.a.s.k.secrets.SecretAsyncClient : Retrieved secret - storageaccountconnectionstring 2022-03-18 10:50:33.382 INFO 125200 --- [nio-8080-exec-1] c.k.javaonazureauthdemo.GetSecret : Secret: xxxxxxx 補足: 環境変数が正しくセットされていない場合 環境変数が正しく設定されていない場合は、以下のように、マネージド ID を使った認証、IntelliJ アカウントを使った認証、、と、あらかじめ決められた順番(先述)に沿って認証が実行されます。これは、Docs にも記載されているように、想定通りの挙動です。 2022-03-15 16:00:49.288 ERROR 21016 --- [nio-8080-exec-1] c.azure.identity.EnvironmentCredential : Azure Identity => ERROR in EnvironmentCredential: Missing required environment variable AZURE_CLIENT_ID 2022-03-15 16:00:49.342 ERROR 21016 --- [nio-8080-exec-1] c.a.i.i.IntelliJCacheAccessor : IntelliJ Authentication not available. Please log in with Azure Tools for IntelliJ plugin in the IDE. 2022-03-15 16:00:49.484 INFO 21016 --- [nio-8080-exec-1] c.a.s.k.secrets.SecretAsyncClient : Retrieving secret - storageaccountconnectionstring 2022-03-15 16:01:01.316 ERROR 21016 --- [ctor-http-nio-2] c.azure.identity.EnvironmentCredential : EnvironmentCredential authentication unavailable. Environment variables are not fully configured. 2022-03-15 16:01:01.318 INFO 21016 --- [ctor-http-nio-2] c.azure.identity.DefaultAzureCredential : Azure Identity => Attempted credential EnvironmentCredential is unavailable. 2022-03-15 16:01:01.341 ERROR 21016 --- [ctor-http-nio-2] c.a.i.implementation.IdentityClient : ManagedIdentityCredential authentication unavailable. Connection to IMDS endpoint cannot be established, Network is unreachable: connect. 2022-03-15 16:01:01.344 ERROR 21016 --- [ctor-http-nio-2] c.a.identity.ManagedIdentityCredential : Azure Identity => ERROR in getToken() call for scopes [https://vault.azure.net/.default]: ManagedIdentityCredential authentication unavailable. Connection to IMDS endpoint cannot be established, Network is unreachable: connect. 2022-03-15 16:01:01.345 INFO 21016 --- [ctor-http-nio-2] c.azure.identity.DefaultAzureCredential : Azure Identity => Attempted credential ManagedIdentityCredential is unavailable. 2022-03-15 16:01:11.660 ERROR 21016 --- [onPool-worker-3] c.a.identity.SharedTokenCacheCredential : Azure Identity => ERROR in getToken() call for scopes [https://vault.azure.net/.default]: SharedTokenCacheCredential authentication unavailable. No accounts were found in the cache. 2022-03-15 16:01:11.663 INFO 21016 --- [onPool-worker-3] c.azure.identity.DefaultAzureCredential : Azure Identity => Attempted credential SharedTokenCacheCredential is unavailable. 2022-03-15 16:01:11.665 ERROR 21016 --- [onPool-worker-3] c.a.i.i.IntelliJCacheAccessor : IntelliJ Authentication not available. Please log in with Azure Tools for IntelliJ plugin in the IDE. 2022-03-15 16:01:11.666 ERROR 21016 --- [onPool-worker-3] com.azure.identity.IntelliJCredential : Azure Identity => ERROR in getToken() call for scopes [https://vault.azure.net/.default]: IntelliJ Authentication not available. Please log in with Azure Tools for IntelliJ plugin in the IDE. 2022-03-15 16:01:11.667 INFO 21016 --- [onPool-worker-3] c.azure.identity.DefaultAzureCredential : Azure Identity => Attempted credential IntelliJCredential is unavailable. 2022-03-15 16:01:12.060 ERROR 21016 --- [onPool-worker-5] c.m.aad.msal4j.PublicClientApplication : [Correlation ID: 793da894-e800-4e09-bf6b-c3dadae35d5a] Execution of class com.microsoft.aad.msal4j.AcquireTokenByAuthorizationGrantSupplier failed. 引用: 『Default Azure credential』 DefaultAzureCredential - 環境変数以外の認証方式 環境変数が設定されていない場合でも、マネージド ID を使った認証、IntelliJ アカウントによる認証、、と、認証フローは遷移します。ただ、開発環境では通っていた認証が Azure 環境へのデプロイ後は通らないなど、認証方式に環境差異が発生してしまう問題が生じます。 それぞれの認証方式で想定される、環境差異に起因する課題を、以下のようにまとめました。DefaultAzureCredential では、環境変数を使った認証が最初に実行されるため、順番は 2 から記述しています。 順番 認証方式 課題 2 マネージド ID ローカル開発時、ローカル PC から Azure Instance Metadata Service (169.254.169.254) への名前解決ができない。 3 IntelliJ 開発マシンに IDE ・拡張機能のインストールが必要。社内規定などで IntelliJ を使えない場合は認証不可。 4 VSCode 開発マシンに IDE ・拡張機能のインストールが必要。社内規定などで VS Code を使えない場合は認証不可。 5 Azure CLI アプリケーションのデプロイ先に Azure CLI のインストールが必要。 参考: 『Azure Instance Metadata Service (Windows)』 上記 2~5 の認証方式は、認証に必要な情報をトークンとして取得するため、環境変数をセットするためのファイルを Git リポジトリで共有する必要がありません。 そのため、誤って認証情報を記述したファイルを Git リポジトリに Push してしまう等、ファイル管理における事故を防ぐ意味では、よりセキュアな認証方式といえます。 ただ、トークンを使った認証方式は、ID トークンの期限切れが発生した場合、現時点の仕様ではアプリケーションやユーザー側でトークンの取得をハンドリングする必要があるため、開発の生産性が落ちてしまうと私は考えています。 一例として、VS Code 認証方式を使用した場合、90 日経過した ID トークンをリフレッシュする機能をサポートしていません(2022年3月18日時点)。より正確に書くと、VS Code バージョン 0.9.11 以前はサポートされていましたが、それ以降のバージョンでは、キャッシュされたリフレッシュトークンを保存する方式に変更が加えられました。 近々この機能はサポートされる予定ですが、それでも Azure へデプロイ後は別の認証方式を採用する必要があります。 2022-03-02 10:07:45.090 ERROR 64800 --- [onPool-worker-5] c.m.aad.msal4j.PublicClientApplication : [Correlation ID: 8bf2bdbb-0c76-45a4-9a66-eb1d371eb864] Execution of class com.microsoft.aad.msal4j.AcquireTokenByAuthorizationGrantSupplier failed. com.microsoft.aad.msal4j.MsalInteractionRequiredException: AADSTS700082: The refresh token has expired due to inactivity.?The token was issued on 2021-11-28T15:18:11.7012365Z and was inactive for 90.00:00:00. よって、環境差異に起因するを認証の手間を減らすためには、環境変数を使った認証を採用することを個人的にはおすすめします。 Azure 上のアプリケーション実行サービスでの環境変数設定方法 補足として、Java に関わらず、App Service 等 Azure 上のアプリケーション実行サービスでももちろん環境変数をセット可能です。 詳細は、以下の Docs を参考にしてください。 Docs: 『App Service アプリを構成する』 参考 Microsoft ID プラットフォームと OAuth 2.0 認証コード フロー MSAL4J JavaDoc microsoft-authentication-library-for-java : Acquiring Token Interactively