Spring Security控制器测试用例返回403状态码求助
微服务添加Spring Security后控制器测试用例返回403禁止访问
问题概述
在微服务中新增Spring Security配置后,控制器的测试用例执行时返回403禁止访问状态码,无法通过预期的201创建状态校验。
控制器代码
@PostMapping("/{username}") @PreAuthorize("hasRole('MEMBER') && #username == authentication.principal.username") public ResponseEntity<Object> addWishlist(@Parameter(hidden = true) @RequestHeader("Authorization") String token, @PathVariable String username, @RequestBody MovieDto movie){ return new ResponseEntity<>(wishlistService.addWishlist(username,movie),HttpStatus.CREATED); }
测试用例代码
@Test @WithMockUser(roles = "MEMBER", username = "user") void testAddWishlist() throws Exception { String username = "user"; MovieDto movieDto = new MovieDto(); movieDto.setTitle("Movie 1"); WishlistDto wishlistDto = new WishlistDto(); wishlistDto.setUsername(username); wishlistDto.setMovies(List.of(movieDto)); when(wishlistService.addWishlist(username, movieDto)).thenReturn(wishlistDto); mockMvc.perform(MockMvcRequestBuilders.post("/api/v1.0/private/wishlist/{username}", username) .header(HttpHeaders.AUTHORIZATION, "Bearer testtoken") .content("{\"id\":\"1\",\"title\":\"Movie 1\"}") .contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isCreated()) .andExpect(MockMvcResultMatchers.jsonPath("$.movies[0].title").value("Movie 1")); }
错误日志
MockHttpServletRequest: HTTP Method = POST Request URI = /api/v1.0/private/wishlist/user Parameters = {} Headers = [Content-Type:"application/json;charset=UTF-8", Authorization:"Bearer testtoken", Content-Length:"28"] Body = {"id":"1","title":"Movie 1"} Session Attrs = {org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken@46bfbbb9, SPRING_SECURITY_CONTEXT=SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=user, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_MEMBER]], Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_MEMBER]]]} Handler: Type = null Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: Attributes = null MockHttpServletResponse: Status = 403 Error message = Forbidden Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", 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 type = null Body = Forwarded URL = null Redirected URL = null Cookies = [] java.lang.AssertionError: Status expected:<201> but was:<403> Expected :201 Actual :403 <Click to see difference> at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59) at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122) at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:637) at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:214) at com.cts.wishlistservice.controller.WishlistControllerTestMvc.testAddWishlist(WishlistControllerTestMvc.java:102) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at java.base/java.util.ArrayList.forEach(ArrayList.java:1597) at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
修复方案
1. 修正控制器权限表达式
控制器中@PreAuthorize使用了HTML转义字符&&,导致SpEL无法正确解析逻辑与操作,权限校验失败。将其替换为Java原生的&&:
@PostMapping("/{username}") @PreAuthorize("hasRole('MEMBER') && #username == authentication.principal.username") public ResponseEntity<Object> addWishlist(@Parameter(hidden = true) @RequestHeader("Authorization") String token, @PathVariable String username, @RequestBody MovieDto movie){ return new ResponseEntity<>(wishlistService.addWishlist(username,movie),HttpStatus.CREATED); }
2. 优化测试用例
- 移除冗余的
Authorization请求头:@WithMockUser已经在测试上下文注入了认证信息,额外添加的token会导致上下文冲突。 - 禁用CSRF校验:Spring Security默认对POST请求要求CSRF token,测试中禁用可简化流程。
修改后的测试用例:
@Test @WithMockUser(roles = "MEMBER", username = "user") void testAddWishlist() throws Exception { String username = "user"; MovieDto movieDto = new MovieDto(); movieDto.setTitle("Movie 1"); WishlistDto wishlistDto = new WishlistDto(); wishlistDto.setUsername(username); wishlistDto.setMovies(List.of(movieDto)); when(wishlistService.addWishlist(username, movieDto)).thenReturn(wishlistDto); mockMvc.perform(MockMvcRequestBuilders.post("/api/v1.0/private/wishlist/{username}", username) .content("{\"id\":\"1\",\"title\":\"Movie 1\"}") .contentType(MediaType.APPLICATION_JSON) .csrf().disable()) .andExpect(MockMvcResultMatchers.status().isCreated()) .andExpect(MockMvcResultMatchers.jsonPath("$.movies[0].title").value("Movie 1")); }
内容的提问来源于stack exchange,提问作者Kollimarla Jagadeep
相关产品推荐
相关产品推荐

