Spring boot 在webflux中实现自定义身份验证管理器时,如何对未经授权的请求响应自定义json正文
我试图实现自定义JWT令牌身份验证,同时还处理全局异常,以便为每种类型的异常定制响应体。一切正常,除了我想在收到未经授权的请求时返回自定义json响应,而不仅仅是401状态码 下面是我对JwtServerAuthenticationConverter和JwtAuthenticationManager的实现Spring boot 在webflux中实现自定义身份验证管理器时,如何对未经授权的请求响应自定义json正文,spring-boot,spring-security,spring-webflux,project-reactor,spring-boot-2,Spring Boot,Spring Security,Spring Webflux,Project Reactor,Spring Boot 2,我试图实现自定义JWT令牌身份验证,同时还处理全局异常,以便为每种类型的异常定制响应体。一切正常,除了我想在收到未经授权的请求时返回自定义json响应,而不仅仅是401状态码 下面是我对JwtServerAuthenticationConverter和JwtAuthenticationManager的实现 @Component public class JwtServerAuthenticationConverter implements ServerAuthenticationConverte
@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))
;
}
}
所以当我在头中用一个无效的JWT令牌点击它时。这是由我的ExceptioHandler类处理的,我得到了下面的输出,非常好。
但是当我用空的jwt令牌击中它时,我得到了这个
现在,我想返回在JWT令牌无效的情况下返回的相同主体。但问题是,当提供空令牌时,它甚至不属于ExceptionHandler类的handle方法。这就是为什么它不在我的控制之下,就像我在同一个类中处理JwtException一样。我该怎么做呢?请帮忙。我自己解决。
webflux提供了ServerAuthenticationFailureHandler来处理自定义响应,但不幸的是ServerAuthenticationFailureHandler无法工作,这是一个已知的问题,因此我创建了一个故障路由,并在其中写入自定义响应,然后设置登录页面
.formLogin()
.loginPage("/auth/failed")
.and()
在convert方法中,如果不存在令牌头,您可以返回一个
mono.error
,这样做有问题。我检查了每个请求是否调用convert方法不满足其安全路径,然后您对如何设置路径有问题,但由于您尚未发布整个图片,我们无法帮助您。您还需要什么?您想让我在此处放置路由器类吗??
@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();
}
}
.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
)
);