我对java比较陌生,当时正在开发我的个人项目。我成功地同步构建了它。只是为了了解更多信息,我想开始使其异步。它是一个包含 Customer、Order 和 OrderItems 实体的订单管理系统。我开始同步的第一个方法是“createOrder”
我的控制器:
@PostMapping("/place")
public CompletableFuture<ResponseEntity<OrderStatusResponse>>
createOrder(@RequestBody OrderRequest orderRequest) {
String username = getAuthenticatedUsername();
return orderService.createOrder(orderRequest , username)
.thenApply(order -> {
OrderStatusResponse orderResponse = new OrderStatusResponse(ORDER_PLACED, order.getOrderId());
return new ResponseEntity<>(orderResponse, HttpStatus.OK);
})
.exceptionally(ex -> {
System.out.println(ex.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
});
}
我的服务:
public CompletableFuture<Orders> createOrder(OrderRequest orderRequest) {
try {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Customer authenticatedCustomer = customerRepository.findByUsername(username)
.orElseThrow(() -> new CustomerNotFoundException("Authenticated customer not found"));
validateOrderRequest(orderRequest);
Orders order = new Orders();
order.setCustomer(authenticatedCustomer);
order.setStatus(ORDER_PLACED);
order.setTimestamp(orderRequest.getTimestamp());
order.setTotalAmount(orderRequest.getTotalAmount());
List<OrderItems> orderItems = orderRequest.getOrderItems().stream()
.map(itemRequest -> new OrderItems(order, itemRequest.getProductId(),
itemRequest.getQuantity(), itemRequest.getPrice()))
.collect(Collectors.toList());
order.setOrderItems(orderItems);
return CompletableFuture.completedFuture(orderRepository.save(order));
} catch (DataIntegrityViolationException e) {
throw new InvalidOrder("Unable to save order. Please check the provided data.");
}
}
我的安全配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig{
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/login" , "/api/register", "/error").permitAll() // Open the register endpoint
.requestMatchers("/api/orders/place").hasRole("USER")
.anyRequest().authenticated() // Protect all other endpoints
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling((exception) ->exception.accessDeniedPage("/error/404"));
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
令人惊讶的是,一开始我的 API 获得了授权,我可以看到下订单的日志。
Hibernate: select c1_0.customer_id,c1_0.email,c1_0.name,c1_0.password,c1_0.phone,c1_0.username from customer c1_0 where c1_0.username=?
Hibernate: select c1_0.customer_id,c1_0.email,c1_0.name,c1_0.password,c1_0.phone,c1_0.username from customer c1_0 where c1_0.username=?
Hibernate: insert into orders (customer_id,status,timestamp,total_amount) values (?,?,?,?)
Hibernate: insert into order_items (order_id,price,product_id,quantity) values (?,?,?,?)
Hibernate: insert into order_items (order_id,price,product_id,quantity) values (?,?,?,?)
Hibernate: insert into order_items (order_id,price,product_id,quantity) values (?,?,?,?)
Hibernate: insert into order_items (order_id,price,product_id,quantity) values (?,?,?,?)
Hibernate: insert into order_items (order_id,price,product_id,quantity) values (?,?,?,?)
Hibernate: insert into order_items (order_id,price,product_id,quantity) values (?,?,?,?)
但是随后没有出现响应,显示“空响应正文”,错误代码为 403。
HTTP/1.1 403
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Thu, 21 Nov 2024 22:00:44 GMT
<Response body is empty>
// 5 seconds because i set a timeout.
Response code: 403; Time: 5442ms (5 s 442 ms); Content length: 0 bytes (0 B)
我不知道为什么会发生这种情况。
我尝试在同一线程中手动存储安全上下文也不起作用。 比如,
SecurityContext context = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(context);
.
.
.
.
.
.
.
SecurityContextHolder.clearContext();
期望保存客户实例,但这并没有发生。 我期待着
的回应{
"status": "ORDER PLACED",
"orderId": x
}
这也不起作用,有什么想法可以解决这个问题吗?
我认为这是因为默认情况下 spring 不会将 SecurityContext 持有者携带到新线程,当您调用 @Async 方法时,Security Context 无法将 SecurityContext 携带到新线程,那么您必须这样做:
@Bean
public InitializingBean initializingBean() {
return () -> SecurityContextHolder.setStrategyName(
SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}