当前位置: 首页 > 知识库问答 >
问题:

如何包装一个OAuth2异常?

萧元徽
2023-03-14

我们有一个使用< code>Spring OAuth2的rest API。用户通过身份验证后,所有JSON响应都采用以下格式:

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

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

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

{"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. 使用具有最高进阶的控制器建议

任何上述方法或新方法的帮助将不胜感激。

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

共有2个答案

郑帅
2023-03-14

我遇到了完全相同的问题并最终解决了一个解决方案。我使用一个自定义的ExceptionHandlerExceptionResolver类作为解析器,它覆盖了getExceptionHandler方法,如下面的代码所示,然后再次使用前置顺序最高的@ControlllerAdments,最终它起作用了。

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;
}
}
蒋鸿文
2023-03-14

如果您想处理身份验证过程,可以设置自己的自定义身份验证管理器

<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>

创建自定义身份验证提供程序,实现AuthsurationProvider

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,您可以将身份验证过程中引发的异常转换为自定义响应消息。

您可以创建的另一个自定义是在授权过程中,方法是创建一个扩展的自定义类。

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>
 类似资料:
  • 有一个应用是这样安装的,以root身份登陆vps(debian11): 我可否为这个应用制作一个docker镜像呢?用docker run来达到我上面那些代码的目的? 报错了 我的镜像源 我的本地代理可以使用 在构建镜像时,使用代理 还是报错

  • 问题内容: 我想将特定类的每个方法包装在python中,并且希望通过最少地编辑该类的代码来实现。我应该怎么做? 问题答案: Michael Foord的Voidspace博客的一个条目中介绍了一种优雅的实现方法,该条目在“方法修饰元类的方法”部分中介绍了什么是元类以及如何使用它们。稍微简化一下,然后将其应用于您的情况会导致以下结果: 在Python中,函数/方法装饰器只是函数包装器和一些语法糖,以

  • 假设网格布局如下: null null 我想‘收缩包装’网格项,这样 1)网格的最大宽度将仅与内容一样宽,但 2)如果没有足够的空间容纳内容-项目将相应地包装。 在上面的演示中,第一个约束失败了--网格列伸展以适应它们各自的内容,并且所有额外的视口宽度在列之间平分(添加)。 Codepen演示-(调整大小看看我在说什么) 我尝试了几种方法来解决这一问题,但它们都将网格项“收缩包装”成与实际网格本身

  • 问题内容: 我收到以下错误: 这是我的组件: 我在这里做错了什么? 问题答案: 由于您已经使用过,因此该组件将无法作为道具使用。由于您已经使用过,作业操作将作为道具提供,您可以像这样使用它们 但是您可以进一步简化它,例如

  • 问题内容: 我知道可以在JUnit中定义一个 “期望的” 异常,方法是: 但是,如果总是抛出相同的异常,却有不同的“嵌套” 原因 ,该怎么办 。 有什么建议? 问题答案: 您可以将测试代码包装在try / catch块中,捕获引发的异常,检查内部原因,记录/断言/任何内容,然后重新抛出异常(如果需要)。

  • 我正在编写一个oauth2提供程序,不确定如何实现客户端注册。oauth2规范没有涵盖这方面: 客户端向授权服务器注册的方法超出了本规范的范围,但通常涉及终端用户与HTML注册表单的交互。 此外,文档对数据模型有以下说明: 通常的做法是将每个客户端与一个现有用户链接起来。无论是否将客户端和用户关联,都要确保能够保护自己免受恶意客户端的攻击。 null