SwaggerのURLをlocalhostから特定のドメインに変更する


Swaggerは本当に便利ですね!
WEBを作る時には、常に入れたいと感じるありがたいFWです。
そのため基本的にはほぼデフォルトのままで十分使えるのですが、「Try it Out!」での接続先「localhost」を変え、検証環境や本番環境のURLを叩きたくなることもあろうかと思います。(あんまないかな?…)
ちょっとハマったので、今回はその際に設定したことを備忘録として残そうと思います。

今回やりたいこと

  • 「Try it Out!」のボタンで検証環境や本番環境のURLを叩きたい
  • ちゃんと閲覧制限も設けたい

環境

  • SpringBoot 1.5.4.RELEASE
  • Java 8
  • Gradle 2.13
  • Swagger(springfox) 2.7.0
  • Nginx 1.6.2

SpringBootにSwaggerを導入

詳しい導入方法に関しては多数の記事がございますので、そちらをご参照いただければと思います。
ここでは、最低限の内容に留めさせていただきます。

build.gradle

build.gradle
.....

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')

    compile('io.springfox:springfox-swagger2:2.7.0')
    compile('io.springfox:springfox-swagger-ui:2.7.0')
}

.....

SwaggerConfig.java

SwaggerConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.Collections;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket swaggerSpringMvcPlugin() {
        return new Docket(DocumentationType.SWAGGER_2)
                .protocols(Collections.singleton("https")) // <- ポイント 1
                .host("example.com") // <- ポイント 2
                .select()
                .apis(RequestHandlerSelectors.basePackage("対象パッケージ"))
                .build()
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfo(
                "APIのタイトル",
                "APIの詳細",
                "バージョン",
                "利用規約のURL",
                new Contact(
                        "名前(会社名とか?)",
                        "会社等のWebサイトURL",
                        "メールアドレス"
                ),
                "ライセンス",
                "ライセンスのURL",
                Collections.emptyList() /* ベンダー拡張( ?(^^; ) */
        );
    }
}

ポイントはDocketに定義されている「protocols」と「host」を使うところです。
もしポートを指定したい場合は「.host("example.com:8080")」と書けばいいようです。
試してはいませんが、protocolsがSetなので、「http」と「https」や、他のURLスキームも可能なのかもしれません。
調べ方が悪かったのか、あまりこの情報が出てこず、ソースを追ったら見つけました♪

閲覧制限設定

ここまで設定して実行(本番にデプロイ)すれば、「Try it Out!」での接続先URLが変わった状態で見れるようになるかと思います。
ただ、それではセキュリティ的に問題が出そうなので、私は以下のような設定を行いました。

default.conf
# 大元設定 {{{
server {
      listen 443 default ssl;
      server_name example.com;
      # 基本設定 {{{
      server_tokens off;

      ssl on;
      ssl_certificate /etc/nginx/conf.d/ssl/example.pem;
      ssl_certificate_key /etc/nginx/conf.d/ssl/example.key;

      # }}}

      # SpringBoot設定 {{{
      location / {
          set $test 1;
          if ($http_user_agent = "ユーザーエージェントで分岐") {
            set $test 0;
          }
          if ($remote_addr = "IPで分岐") {
            set $test 0;
          }
          if ($test = 1) {
              return 404;
          }
          proxy_pass  http://localhost:ポート番号;
      } # }}}
} # }}}

私の場合はちょっと特殊なAPIであるため、全てでUAまたはIPの制限を入れる必要があるので上記のようにしていますが、もちろんSpringBoot側(WebSecurityConfigurerAdapterを継承して作るとか)でもOKですし、Swaggerで使用されるURLのみを制限するという方法でもOKだと思います!

注意点 1

Swaggerで使用されるURLのみを制限する場合、以下を対象にする必要があります。

  • /v2/api-docs
  • /swagger-resources*
  • /swagger-ui.html
  • /webjars/**

「/v2/api-docs」に気づかず苦戦しました。。。
表示後、/v2/api-docsのデータを展開して表示するんですね(>_<)

注意点 2

さすがにセッションまではいい具合にやってもらえません。。。
ここも工夫の余地がありそうです。
例えば特定のURLを叩くとUsernamePasswordAuthenticationTokenをnewしてAuthenticationManager.authenticateでセットしてくれる機能を作るとかですかね?
難しい…(´・ω・`)

まとめ

  • Docketを生成するときに「protocols」と「host」を設定すれば「Try it Out!」のURLを変えられる
  • ポートはhostの引数に直接書く
  • 「/v2/api-docs」から表示データは取得されるため閲覧制限時は注意する
  • セッションまでは上手く操作できないので別途構築を検討する

Swaggerは他にも色々と設定できる項目があるようです。
個人勉強であれば、色々試してみたいですね!