admin管理员组文章数量:1122846
I am relatively new to java and was working on a personal project of mine. I was successfully able to build it synchoronously. Just for learning more I wanted to start making it asynchronous. It's an order management system featuring Customer, Order and OrderItems entity. The first method I started to make synchronous was the 'createOrder'
My Controller:
@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);
});
}
My Service :
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 CompletableFuturepletedFuture(orderRepository.save(order));
} catch (DataIntegrityViolationException e) {
throw new InvalidOrder("Unable to save order. Please check the provided data.");
}
}
My Security Config:
@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();
}
}
Surprisingly at first my API is getting authorised and I can see the logs of an order being placed.
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 (?,?,?,?)
But then the reponse is not appearing shows "Empty Response Body" with an error code of 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)
I have no idea why this is happening.
I tried storing the Security Context manually in the same thread as well did not work. Something like,
SecurityContext context = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(context);
.
.
.
.
.
.
.
SecurityContextHolder.clearContext();
Was expecting the instance of customer to be saved but that did not happen. I was expecting a response of
{
"status": "ORDER PLACED",
"orderId": x
}
That did not work too, any ideas what could fix this?
I am relatively new to java and was working on a personal project of mine. I was successfully able to build it synchoronously. Just for learning more I wanted to start making it asynchronous. It's an order management system featuring Customer, Order and OrderItems entity. The first method I started to make synchronous was the 'createOrder'
My Controller:
@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);
});
}
My Service :
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.");
}
}
My Security Config:
@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();
}
}
Surprisingly at first my API is getting authorised and I can see the logs of an order being placed.
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 (?,?,?,?)
But then the reponse is not appearing shows "Empty Response Body" with an error code of 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)
I have no idea why this is happening.
I tried storing the Security Context manually in the same thread as well did not work. Something like,
SecurityContext context = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(context);
.
.
.
.
.
.
.
SecurityContextHolder.clearContext();
Was expecting the instance of customer to be saved but that did not happen. I was expecting a response of
{
"status": "ORDER PLACED",
"orderId": x
}
That did not work too, any ideas what could fix this?
Share Improve this question edited Nov 21, 2024 at 23:42 devan5hu asked Nov 21, 2024 at 23:40 devan5hudevan5hu 11 bronze badge 3- you can start out by removing the home made security filter, that is not part of spring security and handing out jwts to browsers is dangerous and not recommended hence why there is no such filter in SPring security. I suggest you read the documentation instead of random blogs building homemade security. Second what is the reason for not enabling spring security logs and reading the reason of your 403? – Toerktumlare Commented Nov 22, 2024 at 0:54
- Is there any reason to use CompletableFuture?? The way you are using it seems completely synchronous. There is no @Async anywhere – pebble unit Commented Nov 22, 2024 at 2:01
- Oh my createOrder in the service is @Async that is what is causing the error to be thrown. – devan5hu Commented Nov 22, 2024 at 13:06
1 Answer
Reset to default 0I think this is because by default spring doesn't carry SecurityContext holder to new thread that will when you called @Async method where Security Context is not avilable to carry SecurityContext to new threads then you have to do this:
@Bean
public InitializingBean initializingBean() {
return () -> SecurityContextHolder.setStrategyName(
SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
本文标签: javaForbidden Error when I include Async to a methodStack Overflow
版权声明:本文标题:java - Forbidden Error when I include @Async to a method - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736306507a1932982.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论