使用OAuth2自定义Spring Security的身份验证错误

我想知道我是否可以自定义以下授权错误:

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

当用户请求没有权限时,我得到它。我想将其自定义为与Spring Boot错误非常相似:

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

这可能吗?

非常感谢。


答案 1

接受的答案对我使用Oauth2不起作用。经过一些研究,异常转换器解决方案奏效了。

基本上,您需要创建一个并将其注册为异常转换器。WebResponseExceptionTranslator

首先,创建一个豆类:WebResponseExceptionTranslator

@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"
}

您可以看到我没有从的原始正文中删除和。我建议维护它们,因为这两个字段遵循OAuth2规范。有关更多详细信息,请参阅 RFCOAuth2 API 定义errorerror_descriptionOAuth2Exception

您还可以自定义结果:覆盖 or(只是调用),标识特定的异常以返回不同的 json 结果等。但也有限制:如果你想将某个字段定义为,我认为这是不可能的,因为该方法只接受类型。errorerror_descriptionaddAdditionalInformationinstance ofintegeraddAdditionalInformationString


答案 2

我明白了:)

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