Springdoc Openapiで全APIに認証の設定（security scheme）を指定する方法

## はじめに
たいていのAPIには何らかのセキュリティがかかっているかと思います。

Basic認証やBearerトークンなどがそれです。

そういったAPIに対してsprningdoc openapiを利用しようとした場合、全APIに共通のセキュリティの設定を入れたくなるとおもうので、その設定についての簡単な解説記事です。

## 技術スタック
- spring boot 2.3.x
    - とあるお仕事のためにちょい古にしていますが、2.4.xでも設定方法は同じです。
- java
    - kotlinでも事情はかわりません。サンプルアプリは用意していませんが、同様に動作することを検証済みです。

## 公式ドキュメントによると
[How to configure global security schemes?](https://springdoc.org/#how-to-configure-global-security-schemes)という章に、openapi定義に以下の設定を追加すればグローバルなSecuritySchemeを設定できるよ、とあります。

```java
@Bean
public OpenAPI customOpenAPI() {
    return new OpenAPI().components(new Components()
    .addSecuritySchemes("basicScheme", new SecurityScheme()
    .type(SecurityScheme.Type.HTTP).scheme("basic"))).info(new Info().title("Custom API")
    .version("100")).addTagsItem(new Tag().name("mytag"));
}
```

しかし、これでは動きません。

この設定で、セキュリティのためのボタンは出てくるのですが、これを設定してもAPI呼び出し時にauthorization headerが付与されませんでした。

swagger-uiはこんな感じでした。

![1.springdoc-openapi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306730/4d4826a5-163f-3b57-79b7-2eeef06f7a35.png)


## どうすれば動くか
securiy requirementを追加する必要があります。

設定すると先程のJava Configurationは以下の様に変わります。

```java
@Bean
public OpenAPI customOpenAPI() {
    return new OpenAPI().components(new Components()
    .addSecuritySchemes("basicScheme", new SecurityScheme()
    .type(SecurityScheme.Type.HTTP).scheme("basic")))
    .addSecurityItem(new SecurityRequirement().addList("basicScheme")) // <= これを追加
    .info(new Info().title("Custom API")
    .version("100")).addTagsItem(new Tag().name("mytag"));
}
```

実際に設定するとswagger-uiはこんな感じでした。

![2.springdoc-openapi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306730/c6acef36-89fd-7836-0bd4-6b1f823e1dcb.png)

APIに南京錠のアイコンが増えるようです。
リクエストにもAuthorization Headerが付与されるようになりました。


## 解説
`addSecuritySchemes`と`addSecurityItem`の設定を加えることではswagger-ui上からセキュリティ付きのAPIの実行ができるようになることがわかりました。

解説のために[公式サイト](https://swagger.io/docs/specification/authentication/)から特に重要だと思った部分を以下に引用します。

> After you have defined the security schemes in the securitySchemes section, you can apply them to the whole API or individual operations by adding the security section on the root level or operation level, respectively.　When used on the root level, security applies the specified security schemes globally to all API operations, unless overridden on the operation level.

要約すると、以下のような仕様のようでした。

1. 要求する可能性のあるセキュリティ定義をまず宣言する
    - これだけだと、まだどのAPIにも適用されない
2. OpenAPI仕様書のトップレベルで(1)を利用すると宣言するとすべてのAPIに対してそのセキュリティ定義が適用される
3. ただし、各APIで別のセキュリティ定義の利用を設定すると(2)の設定は上書きできる

この要約で言うところの(2)に該当する設定についての説明がspringdoc openapi公式ページには不足しており、この設定で追加したSecuritySchemeをすべてのAPIに適用したければ、`addSecurityItem`の追加も必要だったということのようです。

公式の書きっぷりも`How to configure global security schemes`という見出しになっており、それをすべてのAPIに適用するとは書いてないので、嘘や誤りではなさそうです。

ただし、openapiのセキュリティ周りの理解が乏しいと私のように「この設定だけで全部のAPIに適用されそうだな」と誤解を招きそうな気がします。

## サンプルアプリ
検証に利用した、実際に動作するサンプルアプリがあるので共有しておきます。

https://github.com/kogayushi/springdoc-openapi-with-global-security-scheme/


## 最後に
まさか[公式の記載](https://springdoc.org/#how-to-configure-global-security-schemes)に不足があると思わず、`addSecurityItem`が必要であることを英語でたくさんググって調べてやっと気づくことができました。

そのため、日本語の情報もどこかに転がっていたほうが助かる人がきっといるのではないかと思っています。

この記事が誰かの役に立ちますように🤞