Spring Security OAuth2でリクエストログを出力する
Spring Security OAuth2でアクセストークンのリクエストとレスポンスのログを出力するには、Apache HttpClientを使うと簡単です。
概要
- Spring Security OAuth2のアクセストークン取得はRestTemplateを利用しています。
- RestTemplateはデフォルトではHttpURLConnectionを利用しますが、HttpClientを利用することもできます。
- HttpClientはログレベルを設定するだけでヘッダやボディのログを出力してくれます。
OAuth2RestTemplateの場合
OAuth2RestTemplateを生成する際に以下を行うことで、アクセストークンのリクエストやレスポンスのログ出力が有効になります。アクセストークン取得後のリクエストやレスポンスは別物なので注意してください。
@Configuration class AppConfiguration { @Bean OAuth2RestTemplate oAuth2RestTemplate(OAuth2ClientContext oAuth2ClientContext) { def restTemplate = new OAuth2RestTemplate(resource(), oAuth2ClientContext) // HttpClientを利用するように設定する def requestFactory = new HttpComponentsClientHttpRequestFactory() def accessTokenProviders = Arrays.asList( new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider() ) accessTokenProviders*.requestFactory = requestFactory restTemplate.accessTokenProvider = new AccessTokenProviderChain(accessTokenProviders) restTemplate } private resource() { def details = new ClientCredentialsResourceDetails() // TODO: client_id, client_secretなどを設定する details } }
@RestController class HelloController { @Inject OAuth2RestTemplate restTemplate @GetMapping('/hello') Hello helloWorld() { // OAuth2RestTemplateを利用してAPIを実行する def response = restTemplate.getForEntity("https://.../hello", Hello) } }
OAuth2FeignRequestInterceptorの場合
OAuth2FeignRequestInterceptorはAccessTokenProviderを設定するメソッドを提供していないため、クラスを継承して拡張する必要があります。
class HelloOAuth2FeignRequestInterceptor extends OAuth2FeignRequestInterceptor { private final OAuth2ClientContext oAuth2ClientContext private final AccessTokenProvider accessTokenProvider = { // HttpClientを利用するように設定する def requestFactory = new HttpComponentsClientHttpRequestFactory() def accessTokenProviders = Arrays.asList( new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider() ) accessTokenProviders*.requestFactory = requestFactory new AccessTokenProviderChain(accessTokenProviders) }() private final OAuth2ProtectedResourceDetails resource = { def details = new ClientCredentialsResourceDetails() // TODO: client_id, client_secretなどを設定する details }() def HelloOAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext) { super(oAuth2ClientContext, null) this.oAuth2ClientContext = oAuth2ClientContext } /** * @see OAuth2FeignRequestInterceptor#acquireAccessToken() */ @Override protected OAuth2AccessToken acquireAccessToken() { // (中略) OAuth2FeignRequestInterceptor#acquireAccessToken() の実装をコピー } }
OAuth2FeignRequestInterceptorに setAccessTokenProvider()
メソッドを追加するPull Requestがあるので、将来のバージョンでは改善されるかもしれません。
ログレベルの設定
HttpClientがログを出力するようにapplication.ymlを設定します。
logging.level: # ヘッダのみ出力する場合 org.apache.http.headers: DEBUG # ヘッダとボディを出力する場合 org.apache.http.wire: DEBUG
アクセストークンリクエストのカスタマイズ(おまけ)
上記と同様に setTokenRequestEnhancer()
を利用することで、アクセストークンのリクエストをカスタマイズできます。例えば、client_id
や client_secret
をボディに入れるといった独自仕様に対応できます。
@Bean OAuth2RestTemplate oAuth2RestTemplate(OAuth2ClientContext oAuth2ClientContext) { def restTemplate = new OAuth2RestTemplate(resource(), oAuth2ClientContext) def accessTokenProvider = new ClientCredentialsAccessTokenProvider() // HttpClientを利用するように設定する accessTokenProvider.requestFactory = new HttpComponentsClientHttpRequestFactory() // アクセストークンリクエストをカスタマイズする accessTokenProviders*.tokenRequestEnhancer = new RequestEnhancer() { @Override void enhance(AccessTokenRequest request, OAuth2ProtectedResourceDetails resource, MultiValueMap<String, String> form, HttpHeaders headers) { } } restTemplate.accessTokenProvider = accessTokenProvider restTemplate }
ご参考まで。
doma-spring-bootのSQL例外変換
doma-spring-bootを利用すると、Doma2の例外クラス(JdbcException
)をSpring Transactionの例外クラス(DataAccessException
)に変換してくれます。例外変換の仕様が明文化されていないようなので調べてみました。どこかにまとめてあったら教えてください。
前提
- doma-spring-boot-1.1
例外変換の仕様
契機 | Doma2の例外クラス → Spring Transactionの例外クラス |
---|---|
楽観的排他制御エラー | OptimisticLockException → OptimisticLockingFailureException |
一意制約違反 | UniqueConstraintException → DuplicateKeyException |
1件であることを期待する検索系SQLの結果が2件以上である場合 | NonUniqueResultException → IncorrectResultSizeDataAccessException |
1列であることを期待する検索系SQLの結果が1列でない場合 | NonSingleColumnException → IncorrectResultSizeDataAccessException |
1件以上存在することを期待する検索系SQLの結果が0件である場合 | NoResultException → EmptyResultDataAccessException |
結果セットに未知のカラムが存在する場合 | UnknownColumnException → TypeMismatchDataAccessException |
結果セットのカラムにマッピングされないプロパティが存在する場合 | ResultMappingException → TypeMismatchDataAccessException |
その他のSQL例外 | JdbcException → SQLExceptionTranslator により変換される |
その他の例外 | JdbcException → UncategorizedDataAccessException |
なお、例外変換はapplication.ymlで以下を設定すると無効化できます。
# Whether convert JdbcException into DataAccessException. doma.exception-translation-enabled: false
共通例外処理を実装する場合などに役に立てば幸いです。
参考資料
2016年の振り返り
2016年もお世話になりました。
概況
振り返り
- 仕事
- Spring CloudとかSwaggerとかAWSとか触ってた。
- JIRA, Confluence, Mattermost, ownCloud, GitBucket, Jenkins, Artifactory, SonarQubeとかをDocker Composeベースで開発基盤に導入してた。
- 上期は暇で、下期は炎上してた。
- 開発
- 上期はGradle SSH Pluginの改善に注力できた。
- 下期はGradle Swagger Generator PluginやDevOps Composeに注力してた。
- GitBucketにJenkinsとの相互運用性を改善するPull Requestを送ったりした。
- プライベート
- 3ヶ月弱の育休を取得した。視野が広がった。
- 家族と過ごす時間を大切にできた。(12月以外)
- 環境の変化に対応しながら家計を管理できた。
計画
エンジニア35歳定年を迎えます。家族と過ごす時間を大切にしつつ、専門性の高い仕事をしていけるように、働く環境を改善していきたいと思います。
2017年もよろしくお願いいたします。