web-dev-qa-db-ja.com

OAuth2を使用してSpringSecurityから認証エラーをカスタマイズする

次の認証エラーをカスタマイズできるかどうか疑問に思いました。

{
  "error": "unauthorized",
  "error_description": "Full authentication is required to access this resource"
}

ユーザーリクエストに権限がない場合に取得します。そして、SpringBootエラーと非常によく似たものになるようにカスタマイズしたいと思います。

{
 "timestamp":1445441285803,
 "status":401,
 "error":"Unauthorized",
 "message":"Bad credentials",
 "path":"/oauth/token"
}

それは可能でしょうか?

どうもありがとう。

10
pakkk

わかった :)

https://stackoverflow.com/a/37132751/2520689

次のように「AuthenticationEntryPoint」を実装する新しいクラスを作成する必要があります。

public class AuthExceptionEntryPoint implements AuthenticationEntryPoint
{
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) throws IOException, ServletException
    {
        final Map<String, Object> mapBodyException = new HashMap<>() ;

        mapBodyException.put("error"    , "Error from AuthenticationEntryPoint") ;
        mapBodyException.put("message"  , "Message from AuthenticationEntryPoint") ;
        mapBodyException.put("exception", "My stack trace exception") ;
        mapBodyException.put("path"     , request.getServletPath()) ;
        mapBodyException.put("timestamp", (new Date()).getTime()) ;

        response.setContentType("application/json") ;
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED) ;

        final ObjectMapper mapper = new ObjectMapper() ;
        mapper.writeValue(response.getOutputStream(), mapBodyException) ;
    }
}

そしてそれを私のResourceServerConfigurerAdapter実装に追加します:

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
{   
    @Override
    public void configure(HttpSecurity http) throws Exception
    {
        http.exceptionHandling().authenticationEntryPoint(new AuthExceptionEntryPoint()) ;

    }
}

必要なものすべてを実装する私のGitHubプロジェクトを見つけることができます。

https://github.com/pakkk/custom-spring-security

5
pakkk

受け入れられた回答は、Oauth2を使用している私には機能しません。いくつかの調査の後、 例外トランスレータソリューション が機能します。

基本的に、WebResponseExceptionTranslatorを作成し、それを例外トランスレータとして登録する必要があります。

まず、WebResponseExceptionTranslatorBeanを作成します。

@Slf4j
@Configuration
public class Oauth2ExceptionTranslatorConfiguration {

    @Bean
    public WebResponseExceptionTranslator oauth2ResponseExceptionTranslator() {
        return new DefaultWebResponseExceptionTranslator() {

            @Override
            public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {

                ResponseEntity<OAuth2Exception> responseEntity = super.translate(e);
                OAuth2Exception body = responseEntity.getBody();
                HttpStatus statusCode = responseEntity.getStatusCode();

                body.addAdditionalInformation("timestamp", dateTimeFormat.format(clock.instant()))
                body.addAdditionalInformation("status", body.getHttpErrorCode().toString())
                body.addAdditionalInformation("message", body.getMessage())
                body.addAdditionalInformation("code", body.getOAuth2ErrorCode().toUpperCase())

                HttpHeaders headers = new HttpHeaders();
                headers.setAll(responseEntity.getHeaders().toSingleValueMap());
                // do something with header or response
                return new ResponseEntity<>(body, headers, statusCode);
            }
        };
    }

}

次に、Oauth2構成を変更してBean WebResponseExceptionTranslatorを登録する必要があります。

@Slf4j
@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private ClientDetailsServiceBuilder builder;

    @Autowired
    private WebResponseExceptionTranslator oauth2ResponseExceptionTranslator;

    @Autowired
    private UserDetailsService userDetailsService;


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) {
        clients.setBuilder(builder);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();

        tokenEnhancerChain.setTokenEnhancers(
                Arrays.asList(tokenEnhancer(), accessTokenConverter()));

        endpoints.tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancerChain)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .exceptionTranslator(oauth2ResponseExceptionTranslator);

    }

}

最終結果は次のようになります。

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource",
    "code": "UNAUTHORIZED",
    "message": "Full authentication is required to access this resource",
    "status": "401",
    "timestamp": "2018-06-28T23:55:28.86Z"
}

OAuth2Exceptionの元の本体からerrorerror_descriptionを削除していないことがわかります。これらの2つのフィールドはOAuth2仕様に従っているため、これらを維持することをお勧めします。詳細については、 RFC および OAuth2 API定義 を参照してください。

結果をカスタマイズすることもできます。errorまたはerror_descriptionをオーバーライドし(addAdditionalInformationを呼び出すだけ)、instance ofで特定の例外を識別して、別のjson結果を返すなど。 。しかし、制限もあります。あるフィールドをintegerとして定義したい場合、addAdditionalInformationメソッドはStringのみを型として受け入れるため、それは不可能だと思います。

3
Dherik

ストーリーショート:https://github.com/melardev/JavaSpringBootOAuth2JwtCrudPagination.git

@ pakkkの応答を読んだ後、同意しなかったので、自分で考えてみることにしましたが、これも失敗しました。そこで、Spring Securityのソースコード自体を確認することにしました。非常に早い段階で呼び出されるフィルター、OAuth2AuthenticationProcessingFilter。このフィルターはヘッダーからJWTを抽出しようとします。例外がスローされると、そのauthenticationEntryPoint.commence()が呼び出されます(@pakkはここにあります)フィルターを追加して、JWTが無効なときに呼び出されるかどうかを確認しようとしました存在し、そうではなかったため、応答を変更するためのカスタムフィルターを追加しても機能しません。次に、OAuth2AuthenticationProcessingFilterが構成されている場所を調べたところ、ResourceServerSecurityConfigurer :: configure(HttpSecurity http)で構成されていることがわかりました。そうは言っても、プロセスにフックする方法を見てみましょう。 Resource ServerアプリケーションでResourceServerConfigurerAdapterクラスを拡張するので、非常に簡単であることがわかります。

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
// ....
}

先に進み、オーバーライドします。

@Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        super.configure(resources);
}

ご覧のとおり、そうです! ResourceServerSecurityConfigurerにアクセスできるので、今は何ですか?さて、デフォルトのエントリポイントを私たちのものに置き換えましょう:

@Autowired
    private AuthenticationEntryPoint oauthEntryPoint;
@Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        super.configure(resources);

        resources.authenticationEntryPoint(oauthEntryPoint);
    }

例を含む完全なソースコードについては、次を参照してください。 https://github.com/melardev/JavaSpringBootOAuth2JwtCrudPagination.git

この手順がないと、少なくとも私にとっては機能せず、@ pakkkによって提供される応答は機能しません。デバッガーをチェックしました。デフォルトでは、使用されるエントリポイントは、次を使用しても私たちのものではありません。

http.and().exceptionHandling().authenticationEntryPoint(oauthEntryPoint)

これは私が最初にテストしたことでした。それを機能させるには、ResourceServerSecurityConfigurerクラスから直接エントリポイントを変更する必要があります。

これが私のエントリポイントです。自分のクラスであるErrorResponseオブジェクトを送信しているので、応答を完全に制御できます。

@Component
public class OAuthEntryPoint implements AuthenticationEntryPoint {

    @Autowired
    ObjectMapper mapper;

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        ServletServerHttpResponse res = new ServletServerHttpResponse(httpServletResponse);
        res.setStatusCode(HttpStatus.FORBIDDEN);
        res.getServletResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        res.getBody().write(mapper.writeValueAsString(new ErrorResponse("You must authenticated")).getBytes());
    }
}
1
Melardev