如何封装OAuth2异常?

5
我们有一个使用Spring OAuth2的REST API。用户通过身份验证后,所有JSON响应都采用以下格式:
{"code" : 12345, "data" : "..." }
但是身份验证失败的JSON响应与上述格式不符,因为它由Spring处理。 例如,如果凭据不正确,则客户端会收到HTTP状态码400,并获得以下JSON响应:
{"error": "invalid_grant", "error_description": "Bad credentials" }
如果用户账户被锁定,客户端将收到HTTP状态码400,并得到以下JSON响应:
{"error":"invalid_grant","error_description":"User account is locked"}
所有这些都是因为Spring TokenEndpoint.handleException()处理与/oauth/token相关的异常。 我想将OAuth2失败的JSON响应更改为遵循第一种格式。 到目前为止,我尝试了以下方法但均未成功: 1.使用ControllerAdvice并具有最高优先级顺序,并使用@ExceptionHandler如here所述 2.实现OAuth2ExceptionRenderer,如here所述 3.实现ExceptionMapper 4.添加一个新的ObjectMapper并扩展StdSerializer。虽然我的objectmapper已初始化,但它没有用于序列化异常。也许是因为Spring直接调用MappingJackson2HttpMessageConverter,而我的应用程序中似乎有几个此类的实例。 非常感谢任何上述方法或新方法上的任何帮助。

我没有尝试this的方法,因为我无法更改现有客户端的上下文路径。

2个回答

1
如果您想处理身份验证过程,可以设置自己的自定义身份验证管理器。
<oauth:authorization-server
    client-details-service-ref="clientDetails" token-services-ref="tokenServices"
    user-approval-handler-ref="userApprovalHandler">
    <oauth:authorization-code />
    <oauth:implicit />
    <oauth:refresh-token />
    <oauth:client-credentials />
    <oauth:password authentication-manager-ref="customAuthenticationManager" />
</oauth:authorization-server>

<authentication-manager id="customAuthenticationManager"
    xmlns="http://www.springframework.org/schema/security">
    <authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>

<bean id="customAuthenticationProvider"
    class="com.any.CustomAuthenticationProvider">
</bean>
创建自定义身份验证提供程序,实现AuthenticationProvider
public class UserAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
        String username = auth.getName();
        String password = token.getCredentials().toString();
        User user = userService.loadByUsername(username);
        if(user.isLocked){
            throw new UserLockedException("User is locked");
        }
        if(another.something.bad.happened){
            throw new AnotherSomethingBadHappenedException("Error");
        }

        // setup authorities
        //...

        return new UsernamePasswordAuthenticationToken(user, password, authorities);
    }


}
现在您有自己的异常,通过使用ExceptionMapper,您可以将身份验证过程中抛出的异常翻译为自定义响应消息。 另一个可创建的定制是通过创建一个扩展ApprovalStoreUserApprovalHandler的自定义类来对授权过程进行定制。
public class CustomUserApprovalHandler extends ApprovalStoreUserApprovalHandler {

    // stripped

    @Override
    public AuthorizationRequest checkForPreApproval(AuthorizationRequest authorizationRequest,
            Authentication userAuthentication) {

        ClientDetails client = clientDetailsService
                            .loadClientByClientId(authorizationRequest.getClientId());
        // here, you have the client and the user
        // you can do any checking here and throw any exception
        authorizationRequest.setApproved(approved);
        return authorizationRequest;
    }
}
创建该类的bean定义。
<bean id="userApprovalHandler"
    class="com.any.CustomUserApprovalHandler">
        <property name="approvalStore" ref="approvalStore" />
        <property name="requestFactory" ref="oAuth2RequestFactory" />
        <property name="clientDetailsService" ref="clientDetails" />
        <property name="useApprovalStore" value="true" />
    </bean>

我该如何使用ExceptionMapper来翻译异常? - Feras Odeh
@FerasOdeh ExceptionMapper是JAX-WS功能,网上有很多示例。如果需要的话最好创建一个新问题。 - KSTN
@MangEngkus 我该如何解决'approvalStore'的问题,因为它们是ApprovalStoreUserApprovalHandler类级别的变量,我遇到了以下异常:Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'approvalStore' is defined。 - Soumyaansh
@Soumyaansh 使用类CustomUserApprovalHandler创建一个名为“approvalStore”的bean。 - KSTN

1
我曾经遇到同样的问题,最终找到了解决方案。我使用了一个自定义的ExceptionHandlerExceptionResolver类作为解析器,并重写了getExceptionHandler方法,如下所示的代码,然后再次使用@ControllerAdvice并设置最高优先级顺序,最终解决了问题。
public class MyExceptionHandlerExceptionResolver extends ExceptionHandlerExceptionResolver {
private Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = null;

@Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
    Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    if (exceptionHandlerAdviceCache==null){
        exceptionHandlerAdviceCache = new LinkedHashMap<ControllerAdviceBean, ExceptionHandlerMethodResolver>();
        for (ControllerAdviceBean adviceBean:adviceBeans){
            ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
            exceptionHandlerAdviceCache.put(adviceBean, resolver);
        }
    }
    for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
        if (entry.getKey().isApplicableToBeanType(handlerType)) {
            ExceptionHandlerMethodResolver resolver = entry.getValue();
            Method method = resolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
            }
        }
    }
    return null;
}
}
在配置中使用MyExceptionHandlerExceptionResolver类。
@EnableWebMvc
@Configuration
public class WebMVCConfiguration extends WebMvcConfigurationSupport {
@Bean
public ExceptionHandlerExceptionResolver handlerExceptionResolver() {
    MyExceptionHandlerExceptionResolver exceptionResolver = new MyExceptionHandlerExceptionResolver();
    exceptionResolver.setOrder(0);
    exceptionResolver.setMessageConverters(messageConverters());
    return exceptionResolver;
}

private MappingJackson2HttpMessageConverter jsonHttpMessageConverter() {
    return new MappingJackson2HttpMessageConverter();
}

private List<HttpMessageConverter<?>> messageConverters() {
    List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
    messageConverters.add(jsonHttpMessageConverter());
    return messageConverters;
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,