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

Java 使用OAuth2从Spring Security自定义身份验证错误,java,security,spring-boot,spring-security,spring-security-oauth2,Java,Security,Spring Boot,Spring Security,Spring Security Oauth2,我想知道是否可以自定义以下授权错误: { "error": "unauthorized", "error_description": "Full authentication is required to access this resource" } 当用户请求没有权限时,我会得到它。我想对其进行定制,使其与Spring引导错误非常相似: { "timestamp":1445441285803, "status":401, "error":"Unauthorized", "m

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

{
  "error": "unauthorized",
  "error_description": "Full authentication is required to access this resource"
}
当用户请求没有权限时,我会得到它。我想对其进行定制,使其与Spring引导错误非常相似:

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


非常感谢。

我认为您可以使用
@ControllerAdvice
捕获未经授权的异常,然后按照您的期望格式化响应并返回它。大概是这样的:

@ResponseBody
@ExceptionHandler(CustomException.class)
@ResponseStatus(value=HttpStatus.UNAUTHORIZED, reason="Exception message")
public JsonResponse unAuthorised(HttpServletRequest request, Exception ex) {
    return new JsonResponse("ERROR", 401, "Unauthorised Request");
}
希望这有帮助。

我明白了:)

我需要创建一个实现“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) ;
    }
}
您可以找到我的GitHub项目,它实现了您需要的一切:


我使用Oauth2时,接受的答案不适用。经过一番研究,这项研究成功了

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

首先,创建一个
WebResponseExceptionTranslator
bean:

@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);
            }
        };
    }

}
最终结果将是:

{
    "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
的原始主体中删除
error
error\u description
。我建议维护它们,因为这两个字段遵循OAuth2规范。有关更多详细信息,请参阅和


您还可以自定义结果:覆盖
错误
错误描述
(只需调用
addAddAdditionalInformation
),使用
实例
识别特定异常以返回不同的json结果,等等。但也有限制:如果要将某个字段定义为
整数
,我认为这是不可能的,因为
addAdditionalInformation
方法只接受
字符串作为类型。

故事简介:

在阅读@pakkk response后,我不同意,因此我决定尝试自己的想法,但也失败了,因此我决定查看Spring安全源代码本身,结果如下: 有一个很早就被调用的过滤器,OAuth2AuthenticationProcessingFilter。 如果抛出它调用的异常,此筛选器将尝试从标头中提取JWT 它的authenticationEntryPoint.comment()(@pakk就在这里) 我尝试添加一个过滤器,以检查当Jwt无效或存在时是否会调用它,但它没有,因此,添加一个自定义过滤器来更改响应将不起作用。 然后我查看了OAuth2AuthenticationProcessingFilter的配置位置,发现它是在ResourceServerSecurityConfigure::configure(HttpSecurity http)上设置的。 话虽如此,让我们来看看我们如何才能融入这个过程。 这非常简单,因为您将在资源服务器应用程序中扩展ResourceServerConfigurerAdapter类:

@配置
@EnableResourceServer
公共类OAuth2ResourceServerConfig扩展了ResourceServerConfigurerAdapter{
// ....
}
您继续并覆盖:

@覆盖
public void configure(ResourceServerSecurityConfigure资源)引发异常{
超级配置(资源);
}
正如你所看到的,是的!您可以访问ResourceServerSecurityConfigure,那么现在怎么办? 让我们用我们的入口点替换默认入口点:

@Autowired
私有身份验证入口点oauthEntryPoint;
@凌驾
public void configure(ResourceServerSecurityConfigure资源)引发异常{
超级配置(资源);
authenticationEntryPoint(oauthEntryPoint);
}
有关完整的源代码和示例,请参见:

如果没有这些步骤,至少对我来说它不会工作,@pakkk提供的响应对我来说不工作,我检查了调试器,默认情况下使用的入口点不是我们的,甚至使用:

http.and().exceptionHandling().authenticationEntryPoint(oauthEntryPoint)
这是我测试的第一件事,要使它工作,您必须直接从ResourceServerSecurityConfigure类更改入口点

这是我的入口点:注意,我正在发送ErrorResponse对象,它是我自己的类,所以我可以完全控制响应:

@组件
公共类OAuthEntryPoint实现AuthenticationEntryPoint{
@自动连线
对象映射器映射器;
@凌驾
public void start(HttpServletRequest HttpServletRequest,HttpServletResponse HttpServletResponse,AuthenticationException e)引发IOException,ServletException{
ServletServerHttpResponse res=新的ServletServerHttpResponse(httpServletResponse);
res.setStatusCode(HttpStatus.FORBIDDEN);
res.getServletResponse().setHeader(HttpHeaders.CONTENT\u TYPE,MediaType.APPLICATION\u JSON\u VALUE);
res.getBody().write(mapper.writeValueAsString(新的错误响应(“您必须经过身份验证”)).getBytes();
}
}

以前的@ControllerAdvice从未捕获此错误类型:(ControllerAdvice将不起作用,因为身份验证是在控制器(包括ControllerAdvice)之前处理的)将被调用。该死的,OAuthException本身在哪里?没有人,AuthenticationEntryPoint是在没有提供凭据时使用的,事实上,Comment方法是不言自明的,它说明了如何告诉用户他应该启动身份验证,这与让他知道凭据无效不同。无论如何,去吧ead将提供无效凭据,您将看到您的入口点未被触发。好吧,我已经查看了源代码,但我错了,但我仍然不同意您的看法(虽然我没有测试您的代码,但我在我的应用程序中使用了类似的设置)。请阅读我的回复,我提供了更详细的信息。@SledgeHammer这在我使用此解决方案的项目上非常有效。请验证您的Spring版本,我使用了一些1.5.X版本。您是否注册了异常转换器?转换器直接注册在Spring配置上
@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"
}