SAP BTP 学習日記 Day9 —— CAP Java + XSUAA が効かない!6層のエラーを一つずつ解決した記録
問題の現象(げんしょう)
BTP へのデプロイと XSUAA サービスインスタンスのバインドは成功していましたが、API アクセスに対して権限(けんげん)制限が全く効いていませんでした。表面上は「XSUAA が効いていない」ように見えましたが、実際には複数の問題が重なっていました。
💡 重要な教訓(きょうくん):XSUAA をバインドするだけでは認証は有効(ゆうこう)になりません。設定ファイル・CDS モデル・Spring コントローラー・依存関係(いぞんかんけい)・エンコード・起動設定の 6 層をすべて正しく設定する必要があります。
プロジェクト構成(こうせい)
このプロジェクトには 2 種類のエンドポイントがあります。これが問題の核心(かくしん)です。
① CAP CDS が自動生成する OData エンドポイント
/odata/v4/CatalogService/Books
② 手書きの Spring MVC エンドポイント
/api/zbooks
/api/salesorders
→ この 2 つは認可(にんか)の仕組みが別々!第 1 層:xs-security.json に問題がある
まず xs-security.json 自体を確認したところ、文字化けや JSON の構造(こうぞう)に問題がありました。ファイルが不正でも BTP 上に service instance は作れるため、「バインド成功」と表示されていても中身が正しいとは限りません。
修正後の xs-security.json
{
"xsappname": "my-cap-java",
"tenant-mode": "dedicated",
"scopes": [
{
"name": "$XSAPPNAME.Books.Read",
"description": "Read access for Books"
},
{
"name": "$XSAPPNAME.Books.Write",
"description": "Write access for Books"
}
],
"role-templates": [
{
"name": "Viewer",
"description": "Read only",
"scope-references": ["$XSAPPNAME.Books.Read"]
},
{
"name": "Editor",
"description": "Read and write",
"scope-references": [
"$XSAPPNAME.Books.Read",
"$XSAPPNAME.Books.Write"
]
}
]
}修正後は BTP 上のサービスインスタンスも更新する必要があります:
cf update-service my-cap-java-xsuaa -c xs-security.json第 2 層:CDS サービスに認可ルールがない
XSUAA が正常に動いていても、CDS サービス側に認可ルールがなければ誰でもアクセスできます。@requires と @restrict を追加します。
修正後の cat-service.cds
@requires: 'authenticated-user'
service CatalogService {
@restrict: [
{ grant: 'READ', to: ['Viewer', 'Editor'] },
{ grant: ['CREATE', 'UPDATE', 'DELETE'], to: 'Editor' }
]
entity Books as projection on my.Books;
}第 3 層:Spring 独自エンドポイントは CDS 認可が効かない
手書きの @RestController エンドポイントは CDS の @restrict の対象(たいしょう)外です。別途(べっと)Spring Security の @PreAuthorize を追加する必要があります。
ZBookController.java
@RestController
@RequestMapping("/api")
public class ZBookController {
@Autowired
private ZBookService zBookService;
@GetMapping(value = "/zbooks", produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('my-cap-java.Books.Read')")
public ResponseEntity<String> getBooks() {
try {
String result = zBookService.getBooks();
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(result);
} catch (Exception e) {
return ResponseEntity.status(500)
.body("{"error": "" + e.getMessage() + ""}");
}
}
}MethodSecurityConfig.java(新規作成)
@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
}第 4 層:UTF-8 BOM によるコンパイルエラー
新しく作成・編集したファイルに UTF-8 BOM が含まれていると Maven コンパイル時に以下のエラーが出ます:
illegal character: ''以下のファイルを UTF-8 without BOM で保存し直す必要があります:
- MethodSecurityConfig.java
- SalesOrderController.java
- ZBookController.java
- xs-security.json
- cat-service.cds
第 5 層:Spring Security 関連の依存関係が不足
@EnableMethodSecurity を使うには追加の依存関係が必要です。srv/pom.xml に以下を追加します:
<!-- CAP XSUAA 認証 -->
<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-feature-identity</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- XSUAA Resource Server -->
<dependency>
<groupId>com.sap.cloud.security</groupId>
<artifactId>resourceserver-security-spring-boot-starter</artifactId>
<version>3.3.5</version>
</dependency>
<!-- Spring Security Config -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>第 6 層:application.yaml の手書き設定が起動失敗の原因
以下のような手書きの issuer-uri 設定は BTP 上で起動失敗の原因になりやすいです:
# ❌ 良くない書き方
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${vcap.services.my-cap-java-xsuaa.credentials.url}/oauth/token代わりに CAP Java の自動設定に任せます:
# ✅ シンプルな書き方
cds:
security:
authentication:
mode: model-strictBTP 側でも必要な作業
xs-security.json を修正しても BTP 上のサービスインスタンスは自動更新されません。以下のコマンドで手動更新(しゅどうこうしん)が必要です:
# XSUAA サービスインスタンスを更新
cf update-service my-cap-java-xsuaa -c xs-security.json
# 再デプロイ
mvn package -DskipTests && cf pushまた、BTP Cockpit でテストユーザーにロールコレクションを割り当て(わりあて)ることも忘れずに!
今回の修正内容まとめ
| 層 | 問題 | 修正内容 |
|---|---|---|
| ① xs-security.json | 文字化け・構造エラー | 正しい JSON に書き直す |
| ② CDS サービス | 認可ルールがない | @requires と @restrict を追加 |
| ③ Spring エンドポイント | CDS 認可が効かない | @PreAuthorize を追加 |
| ④ UTF-8 BOM | コンパイルエラー | BOM なし UTF-8 で保存 |
| ⑤ 依存関係不足 | Security クラスが見つからない | pom.xml に 4 つの依存関係を追加 |
| ⑥ application.yaml | 手書き設定で起動失敗 | CAP 自動設定に任せる |
XSUAA トラブル時の排査(はいさ)順序
① xs-security.json は合法(ごうほう)か
② BTP 上の XSUAA service instance は更新済みか
③ テストユーザーにロールが割り当てられているか
④ CDS サービスに @requires / @restrict が書いてあるか
⑤ Spring 独自エンドポイントに @PreAuthorize があるか
⑥ application.yaml が自動設定と競合(きょうごう)していないか
⑦ BOM・依存関係・コンパイルエラーがないか今日の学び
「XSUAA が効かない」という問題は一箇所(いっかしょ)を直せば解決するものではありませんでした。設定ファイル・CDS モデル・Spring コントローラー・依存関係・エンコード・起動設定という認証チェーン全体を正しく繋ぐ必要があります。
特に重要な発見(はっけん)は、CAP の OData エンドポイントと Spring の独自エンドポイントでは認可の仕組みが異なるという点です。両方に別々の対応が必要です。
この記事は「SAP BTP 学習」シリーズの第9回です。6ヶ月間の独学で SAP BTP 開発へのキャリアチェンジを目指しています。