【问题标题】:How to wrap an OAuth2 exception?如何包装 OAuth2 异常?
【发布时间】:2015-10-27 09:23:39
【问题描述】:

我们有一个使用 Spring OAuth2 的 REST API。用户通过身份验证后,所有 JSON 响应格式如下:

{"code" : 12345, "data" : "..." }

但是验证失败的 JSON 响应不符合上述格式,因为它是由 Spring 处理的。

例如,在凭据不正确的情况下,客户端会得到 带有 JSON 响应的 HTTP 状态码 400 如下:

{"error": "invalid_grant", "error_description": "Bad credentials" }

如果用户帐户被锁定,客户端会收到带有 JSON 响应的 HTTP 状态代码 400,如下所示

{"error":"invalid_grant","error_description":"User account is locked"}

这一切都是因为 Spring TokenEndpoint.handleException() 正在处理与 /oauth/token 相关的异常

我想更改 OAuth2 失败的 JSON 响应以遵循第一种格式。

这是我迄今为止尝试过的,但没有成功:

  1. 使用具有最高优先级的 ControllerAdvice 并使用 @ExceptionHandler,如 here 所述
  2. 按照here 的描述实现 OAuth2ExceptionRenderer
  3. 实现异常映射器
  4. 添加了一个新的 ObjectMapper,扩展了 StdSerializer。尽管我的 objectmapper 已初始化,但它并未用于序列化异常。可能是因为 Spring 直接调用 MappingJackson2HttpMessageConverter 并且我的应用中似乎有这个类的多个实例。

对于上述任何一种方法或新方法的任何帮助,我们将不胜感激。

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

【问题讨论】:

    标签: java json spring oauth spring-security-oauth2


    【解决方案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翻译异常?
    • @FerasOdeh ExceptionMapper 是 JAX-WS 功能,互联网上有很多示例。如果需要,最好创建一个新问题
    • @MangEngkus 我如何解析“approvalStore”,因为它们是 ApprovalStoreUserApprovalHandler 的类级别变量,并且我遇到了异常,原因是:org.springframework.beans.factory.NoSuchBeanDefinitionException:没有名为“的 bean”定义approvalStore'
    • @Soumyaansh 使用类 CustomUserApprovalHandler 创建一个名为“approvalStore”的 bean
    【解决方案2】:

    我遇到了完全相同的问题并最终找到了解决方案。 我使用自定义的 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;
    }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-09
      • 2016-01-20
      • 2021-01-05
      • 1970-01-01
      • 2011-01-30
      • 2017-08-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多