@AuthenticationPrincipal 对象返回之前存储在会话中的值。
Spring Boot + Spring Security oauth REST 服务器。https://github.com/legshort/spring-boot-sample
这两种 REST 方法在控制器中。问题是当我运行测试代码时,deleteUser() 处的最后一个参数 userDetailsImpl 与 updateUser() 处的 userDetailsImpl 的值相同。
@RequestMapping(method = RequestMethod.PUT, value = "/users/{userId}")
public ResponseEntity updateUser(@PathVariable Long userId,
@AuthenticationPrincipal UserDetailsImpl userDetailsImpl,
@Valid @RequestBody UserUpdateForm userUpdateForm,
BindingResult bindingResult) {
logger.info("UserUpdate: " + userUpdateForm);
User updatedUser = userService.updateUser(userUpdateForm
.createUser(userId));
return new ResponseEntity(updatedUser, HttpStatus.OK);
}
@RequestMapping(method = RequestMethod.DELETE, value = "/users/{userId}")
public ResponseEntity deleteUser(@PathVariable Long userId,
@AuthenticationPrincipal UserDetailsImpl userDetailsImpl) {
logger.info("UserDelete: " + userId);
User requestedUser = new User(userId);
userService.deleteUser(requestedUser);
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
下面是控制器测试代码
我不知道第二个请求 testDeleteUser() 如何具有会话值,并且它是使用先前测试的同一用户。因此,即使在 deleteUser() 的开头验证访问令牌并加载正确的新用户,但 userDetailsImpl 的实际值在 testUpdateUser() 的开头创建了错误的用户。
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilters(filterChainProxy).build();
}
@Test
public void testUpdateUser() throws Exception {
User savedUser = signUpUser();
// @formatter:off
mockMvc.perform(
put("/users/" + savedUser.getId())
.header(HeaderUtil.AUTHORIZATION, getAuthorizationWithAccessToken())
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(UserUpdateFormFactory.newInstance())))
.andExpect(status().isOk())
.andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.id", is(greaterThan(NumberUtils.INTEGER_ZERO))))
.andExpect(jsonPath("$.name", is(equalTo(StringUtil.NEW + UserFactory.NAME))));
// @formatter:on
}
@Test
public void testDeleteUser() throws Exception {
User savedUser = signUpUser();
String authorization = getAuthorizationWithAccessToken();
// @formatter:off
mockMvc.perform(
delete("/users/" + savedUser.getId())
.header(HeaderUtil.AUTHORIZATION, authorization)
.contentType(TestUtil.APPLICATION_JSON_UTF8))
.andDo(print())
.andExpect(status().isNoContent());
// @formatter:on
}
这是 UserDetailService 实现,当涉及到 loadUserByUserName() 来验证访问令牌时,它会从数据库中加载正确的用户并返回刚刚在每个测试方法开始时创建的新用户(signUpUser())。
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
User requestedUser = new User();
requestedUser.setEmail(email);
User savedUser = userService.findByEmail(requestedUser);
return new UserDetailsImpl(savedUser);
}
}
我试图禁用我失败的会话,对我来说配置和测试代码似乎很好。spring-security-oauth 有什么好的实际例子吗?
更新
据我了解 mockMvc 是,它清除所有设置,并且每次使用 setUp() 方法创建几乎新的模拟服务器。因此,每次都应该清除访问令牌存储,但令牌存储会以某种方式维护经过身份验证的令牌。
在测试期间询问使用“/oauth/token”请求的访问令牌,以下是调用 InMemoryTokenStore 的方式。
测试过程日志
testUpdateUser() -> POST:/oauth/token -> 存储令牌
令牌:50b10897-9e15-4859-aeb0-43d0802ba42c
用户:id=2testUpdateUser() -> PUT: /users/2 -> 读取令牌
令牌:50b10897-9e15-4859-aeb0-43d0802ba42c
用户:id=2testUpdateUserWithWrongUserId() -> GET: /oauth/token -> 存储令牌
令牌:50b10897-9e15-4859-aeb0-43d0802ba42c -> 已经存在于令牌
用户中:id=2 -> id=4: 用户已更新为新用户testUpdateUserWithWrongUserId() -> PUT:/users/0 -> 读取令牌
令牌:50b10897-9e15-4859-aeb0-43d0802ba42c
用户:id=2testDeleteUser() -> GET: /oauth/token -> 没有存储令牌,应该存储令牌
testDeleteUser() -> DELETE: /users/5 -> 读取令牌
令牌:50b10897-9e15-4859-aeb0-43d0802ba42c
用户:id=2 -> 用户应该是 id=5,它是用 userSignUp() 创建的
问题
如何使用 mockMvc 清除每个测试方法的 InMemoryTokenStore?