如何在 webflux 中实现自定义身份验证管理器时对未经授权的请求响应自定义 json 正文

     2023-02-27     40

关键词:

【中文标题】如何在 webflux 中实现自定义身份验证管理器时对未经授权的请求响应自定义 json 正文【英文标题】:How to response custom json body on unauthorized requests while implementing custom authentication manager in webflux 【发布时间】:2020-11-27 03:52:15 【问题描述】:

我正在尝试实现自定义 JWT 令牌身份验证,同时我也在处理全局异常以自定义每种异常类型的响应正文。一切正常,除了我想在收到未经授权的请求时返回自定义 json 响应,而不仅仅是 401 状态码。

下面是我对 JwtServerAuthenticationConverter 和 JwtAuthenticationManager 的实现。

@Component
public class JwtServerAuthenticationConverter implements ServerAuthenticationConverter 

    private static final String AUTH_HEADER_VALUE_PREFIX = "Bearer ";

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange) 

        return Mono.justOrEmpty(exchange)
                .flatMap(serverWebExchange -> Mono.justOrEmpty(
                        serverWebExchange
                                .getRequest()
                                .getHeaders()
                                .getFirst(HttpHeaders.AUTHORIZATION)
                        )
                )
                .filter(header -> !header.trim().isEmpty() && header.trim().startsWith(AUTH_HEADER_VALUE_PREFIX))
                .map(header -> header.substring(AUTH_HEADER_VALUE_PREFIX.length()))
                .map(token -> new UsernamePasswordAuthenticationToken(token, token))
                ;
    

@Component
public class JwtAuthenticationManager implements ReactiveAuthenticationManager 

    private final JWTConfig jwtConfig;
    private final ObjectMapper objectMapper;

    public JwtAuthenticationManager(JWTConfig jwtConfig, ObjectMapper objectMapper) 
        this.jwtConfig = jwtConfig;
        this.objectMapper = objectMapper;
    

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) 

        return Mono.just(authentication)
                .map(auth -> JWTHelper.loadAllClaimsFromToken(auth.getCredentials().toString(), jwtConfig.getSecret()))
                .onErrorResume(throwable -> Mono.error(new JwtException("Unauthorized")))
                .map(claims -> objectMapper.convertValue(claims, JWTUserDetails.class))
                .map(jwtUserDetails ->
                        new UsernamePasswordAuthenticationToken(
                                jwtUserDetails,
                                authentication.getCredentials(),
                                jwtUserDetails.getGrantedAuthorities()
                        )
                )
                ;
    

下面是我的全局异常处理,除了 webflux 从 JwtServerAuthenticationConverter 转换方法返回 401 的情况外,它工作得非常好。

@Configuration
@Order(-2)
public class ExceptionHandler implements WebExceptionHandler 

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) 

        exchange.getResponse().getHeaders().set("Content-Type", MediaType.APPLICATION_JSON_VALUE);

        return buildErrorResponse(ex)
                .flatMap(
                        r -> r.writeTo(exchange, new HandlerStrategiesResponseContext(HandlerStrategies.withDefaults()))
                );
    

    private Mono<ServerResponse> buildErrorResponse(Throwable ex) 

        if (ex instanceof RequestEntityValidationException) 

            return ServerResponse.badRequest().contentType(MediaType.APPLICATION_JSON).body(
                    Mono.just(new ErrorResponse(ex.getMessage())),
                    ErrorResponse.class
            );

         else if (ex instanceof ResponseStatusException) 
            ResponseStatusException exception = (ResponseStatusException) ex;

            if (exception.getStatus().value() == 404) 
                return ServerResponse.status(HttpStatus.NOT_FOUND).contentType(MediaType.APPLICATION_JSON).body(
                        Mono.just(new ErrorResponse("Resource not found - 404")),
                        ErrorResponse.class
                );
             else if (exception.getStatus().value() == 400) 
                return ServerResponse.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON).body(
                        Mono.just(new ErrorResponse("Unable to parse request body - 400")),
                        ErrorResponse.class
                );
            

         else if (ex instanceof JwtException) 

            return ServerResponse.status(HttpStatus.UNAUTHORIZED).contentType(MediaType.APPLICATION_JSON).body(
                    Mono.just(new ErrorResponse(ex.getMessage())),
                    ErrorResponse.class
            );
        

        ex.printStackTrace();
        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(MediaType.APPLICATION_JSON).body(
                Mono.just(new ErrorResponse("Internal server error - 500")),
                ErrorResponse.class
        );

    


@RequiredArgsConstructor
class HandlerStrategiesResponseContext implements ServerResponse.Context 

    private final HandlerStrategies handlerStrategies;

    @Override
    public List<HttpMessageWriter<?>> messageWriters() 
        return this.handlerStrategies.messageWriters();
    

    @Override
    public List<ViewResolver> viewResolvers() 
        return this.handlerStrategies.viewResolvers();
    


@Configuration
@EnableWebFluxSecurity
public class SecurityConfig 

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(
            ServerHttpSecurity http,
            ReactiveAuthenticationManager jwtAuthenticationManager,
            ServerAuthenticationConverter jwtAuthenticationConverter
    ) 

        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(jwtAuthenticationManager);
        authenticationWebFilter.setServerAuthenticationConverter(jwtAuthenticationConverter);

        return http
                .authorizeExchange()
                .pathMatchers("/auth/login", "/auth/logout").permitAll()
                .anyExchange().authenticated()
                .and()
                .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .httpBasic()
                .disable()
                .csrf()
                .disable()
                .formLogin()
                .disable()
                .logout()
                .disable()
                .build();
    

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() 
        return new BCryptPasswordEncoder();
    

所以当我在标头中使用无效的 JWT 令牌点击它时。这由我的 ExceptionHandler 类处理,我得到了下面的输出,这很棒。

但是当我用空的 jwt 令牌点击它时,我得到了这个。

现在我想返回在 JWT 令牌无效的情况下返回的相同正文。但问题是当提供空令牌时,它甚至不属于 ExceptionHandler 类的句柄方法。这就是为什么它不像我在同一个班级中对 JwtException 所做的那样在我的控制范围内。请问我该怎么做?

【问题讨论】:

在您的转换方法中,如果不存在令牌标头,您可以返回 mono.error 这样做有问题。我检查了每个请求都调用了 convert 方法,不管它的安全路径与否 那么您的路径设置有问题,但由于您没有发布整张图片,我们无法为您提供帮助。 你还需要什么?? 你要我把路由器类放在这里吗?? 【参考方案1】:

我自己解决。 webflux 提供 ServerAuthenticationFailureHandler 来处理自定义响应,但不幸的是 ServerAuthenticationFailureHandler 不起作用,这是一个已知问题,所以我创建了一个失败路由并在其中写入我的自定义响应并设置登录页面。

.formLogin()
.loginPage("/auth/failed")
.and()
.andRoute(path("/auth/failed").and(accept(MediaType.APPLICATION_JSON)), (serverRequest) ->
        ServerResponse
                .status(HttpStatus.UNAUTHORIZED)
                .body(
                        Mono.just(new ErrorResponse("Unauthorized")),
                        ErrorResponse.class
                )
);

【讨论】:

如何在 ASP.NET MVC 5 中实现自定义身份验证

】如何在ASP.NETMVC5中实现自定义身份验证【英文标题】:HowtoimplementcustomauthenticationinASP.NETMVC5【发布时间】:2015-10-1314:09:48【问题描述】:我正在开发一个ASP.NETMVC5应用程序。我有一个现有的数据库,我从中创建了我的ADO.NET实体... 查看详情

我们如何在 Laravel 中实现自定义的仅 API 身份验证

】我们如何在Laravel中实现自定义的仅API身份验证【英文标题】:HowdoweimplementcustomAPI-onlyauthenticationinLaravel【发布时间】:2020-09-1019:04:42【问题描述】:这不是一个非常需要答案的问题,但欢迎提供进一步的建议和答案和建议。我... 查看详情

如何在 ASP.NET MVC 中实现自定义主体和身份?

】如何在ASP.NETMVC中实现自定义主体和身份?【英文标题】:HowdoIimplementcustomPrincipalandIdentityinASP.NETMVC?【发布时间】:2011-03-1711:30:25【问题描述】:我想在经过身份验证的用户中存储额外的信息,以便我可以轻松访问它(例如User.... 查看详情

可以在 Firebase 3 中实现自定义身份验证属性并将其与安全安全规则一起使用吗?

】可以在Firebase3中实现自定义身份验证属性并将其与安全安全规则一起使用吗?【英文标题】:CancustomauthpropertiesbeimplementedandusedwithsecuritysecurityrulesinFirebase3?【发布时间】:2016-09-1916:15:30【问题描述】:legacyFirebasedocumentation表示... 查看详情

如何在 ASP.NET Core 中实现自定义模型验证?

】如何在ASP.NETCore中实现自定义模型验证?【英文标题】:ASP.NETCore-Createcustommodelvalidation【发布时间】:2016-07-1215:07:56【问题描述】:在以前版本的ASP.NETMVC中,向模型添加自定义验证的方法是实现IValidatableObject并实现您自己的Val... 查看详情

在 MVC 中实现自定义身份和 IPrincipal

】在MVC中实现自定义身份和IPrincipal【英文标题】:ImplementingaCustomIdentityandIPrincipalinMVC【发布时间】:2010-12-2510:29:21【问题描述】:我有一个基本的MVC2beta应用程序,我正在尝试实现自定义身份和主体类。我创建了实现IIdentity和IPr... 查看详情

如何在 Spring 身份验证管理器之前执行自定义处理程序

】如何在Spring身份验证管理器之前执行自定义处理程序【英文标题】:HowtoexecutecustomhandlerbeforeSpringauthenticationmanager【发布时间】:2013-11-0604:57:11【问题描述】:我想知道是否可以在spring身份验证管理器之前执行自定义处理程序... 查看详情

如何在 Wildfly 中提供自定义身份验证/授权管理器

】如何在Wildfly中提供自定义身份验证/授权管理器【英文标题】:Howtoprovidecustomathentication/authorizationmanagerinWildfly【发布时间】:2016-01-1022:53:21【问题描述】:我有一个提供身份验证和授权的后端REST服务。如何配置我的Wildfly服务... 查看详情

如何在 keras 中实现自定义指标?

】如何在keras中实现自定义指标?【英文标题】:howtoimplementcustommetricinkeras?【发布时间】:2016-10-0601:13:54【问题描述】:我得到这个错误:sum()得到了一个意外的关键字参数\'out\'当我运行这段代码时:importpandasaspd,numpyasnpimportkera... 查看详情

如何在 iPhone 中实现自定义相机功能? [关闭]

】如何在iPhone中实现自定义相机功能?[关闭]【英文标题】:HowtoimplementcustomcamerafunctionalityiniPhone?[closed]【发布时间】:2011-08-1605:07:13【问题描述】:我想开发一种功能,以便在iPhone应用程序中实现自定义相机功能,所以请给我... 查看详情

如何使用 Windows 身份验证并注册自定义身份验证管理器

】如何使用Windows身份验证并注册自定义身份验证管理器【英文标题】:Howtousewindowsauthenticationandregistercustomauthmanager【发布时间】:2011-08-0117:55:38【问题描述】:我正在努力保护RESTFul服务。所有客户端都是windows内网客户端,我想... 查看详情

在 MembershipProvider 中实现自定义“ValidateUser”

】在MembershipProvider中实现自定义“ValidateUser”【英文标题】:Implementcustom"ValidateUser"inMembershipProvider【发布时间】:2011-03-2510:32:38【问题描述】:我正在实现一个自定义MembershipProvider,我正在尝试使用ValidateUser方法来验证... 查看详情

如何在黄瓜中实现自定义监听器?

】如何在黄瓜中实现自定义监听器?【英文标题】:Howtoimplementcustomlistenersincucumber?【发布时间】:2021-12-2214:55:47【问题描述】:如何在cucumber中实现客户监听?哪个可以记录到控制台/报告失败方法的发生?使用黄瓜4.0注意:钩... 查看详情

如何在 TCPDF 中实现自定义字体

】如何在TCPDF中实现自定义字体【英文标题】:HowtoimplementcustomfontsinTCPDF【发布时间】:2011-07-1222:11:47【问题描述】:在TCPDF中,只有几种字体可供选择,用于创建pdf文件。我想将Tahoma设置为我的pdf字体。如何在TCPDF中包含Tahoma?... 查看详情

如何在canvas中实现自定义路径动画(代码片段)

...塞尔曲线,因此,这个动画也许是下面这个样子的:那么如何才能在canvas中实现这种动画效果呢?其实很简单,对于路径的处理svg非常在行,因此在canvas中实现自定义路径动画,我们需要借助svg的力量。创建Path制作动画前,先... 查看详情

在身份验证 Spring Security + WebFlux 期间抛出和处理自定义异常

】在身份验证SpringSecurity+WebFlux期间抛出和处理自定义异常【英文标题】:ThrowandhandlecustomexceptionduringauthenticationSpringSecurity+WebFlux【发布时间】:2019-06-0503:57:03【问题描述】:我试图在身份验证期间在WebFlux中引发自定义异常,并... 查看详情

如何在 UIImagePicker 中实现自定义“使用”和“重拍”按钮?

】如何在UIImagePicker中实现自定义“使用”和“重拍”按钮?【英文标题】:Howtoimplementcustom\'Use\'and\'Retake\'buttoninUIImagePicker?【发布时间】:2011-03-2507:06:04【问题描述】:我的意思是,我已经关闭了相机控制器pickerCam.showsCameraContro... 查看详情

如何在自定义 Spring 存储库中实现自定义方法? [复制]

】如何在自定义Spring存储库中实现自定义方法?[复制]【英文标题】:Howtoimplementcustommethodincustomspringrepository?[duplicate]【发布时间】:2016-03-2719:36:21【问题描述】:假设我想要一个方法,获取超级主客户,有id=0。我有客户类:@Ent... 查看详情