3

@AuthenticationPrincipal object возвращает предыдущее значение, которое было сохранено в сеансе.@AuthenticationPrincipal object return session value

Весенний ботинок + весна безопасности oauth REST Server. https://github.com/legshort/spring-boot-sample

Эти два метода REST находятся в контроллере. Проблема в том, что последний аргумент userDetailsImpl в deleteUser() имеет то же значение, что и userDetailsImpl при updateUser(), когда я запускаю тестовый код.

@RequestMapping(method = RequestMethod.PUT, value = "https://stackoverflow.com/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 = "https://stackoverflow.com/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("https://stackoverflow.com/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("https://stackoverflow.com/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); 
    } 
} 

Я попытался отключить сеанс, который я потерпел неудачу, кажется мне хорошо с конфигурацией и тестовым кодом для меня. Есть ли хороший практический пример для весны-безопасности-ов?


ОБНОВЛЕНО

  1. Насколько я понимаю, о mockMvc, оно сбрасывает все установки, и это создает довольно много нового Мок сервера каждый раз с методом нАлАдкА(). Следовательно, хранилище доступа к токенам должно быть очищено каждый раз, но в некотором смысле хранилище токенов поддерживает аутентифицированные токены.

  2. Запросить токен доступа, запрошенный с помощью «/ oauth/token», который был пропущен во время теста, ниже описано, как вызывается InMemoryTokenStore.


Процесс испытания Вход

  1. testUpdateUser() -> ПОСТ:/OAuth/маркер -> магазин маркер
    маркер: 50b10897-9e15-4859-aeb0-43d0802ba42c
    пользователь: id = 2

  2. testUpdateUser() -> PUT:/users/2 -> чтения маркеров
    маркера: 50b10897-9e15-4859-aeb0-43d0802ba42c
    пользователя: ID = 2

  3. testUpdateUserWithWrongUserId() -> GET:/OAuth/маркер -> магазин маркер
    маркер: 50b10897-9e15- 4859-aeb0-43d0802ba42c -> уже существует в знак
    пользователя: ID = 2 -> ID = 4: пользователь был обновлен с новым одним

  4. testUpdateUserWithWrongUserId() -> PUT:/пользователей/0 -> прочитать токен
    токен: 50b10897-9e15-4859-aeb0-43d0802ba42c
    пользователь: ID = 2

  5. testDeleteUser() -> GET:/OAuth/маркер -> не хранить маркер, должен хранить маркер

  6. testDeleteUser() -> DELETE:/Пользователи/5 - > прочитать маркер
    маркер: 50b10897-9e15-4859-aeb0-43d0802ba42c
    пользователя: ID = 2 -> пользователь должен был ID = 5, который был создан с userSignUp()


Вопросы
Как очистить InMemoryTokenStore каждый метод тестирования с помощью mockMvc?

+0

Вы используете только один экземпляр wac (WebApplicationContext), поэтому главный хранитель там хранится между двумя испытаниями. Почему бы просто не устанавливать директиву явно в каждом тесте, например 'delete ("/users/"+ savedUser.getId()). Main (null)' ?? –

+0

AuthenticationPrincipal предназначен для обеспечения безопасности SecurityContextHolder.getContext(). GetAuthentication(). GetPrincipal(), поэтому это ожидаемое поведение. Наверное, я изо всех сил пытаюсь понять, что вы хотите от своего кода. –

+0

@RobWinch благодарит за ваш комментарий. Я хочу протестировать каждый метод с чистым статусом, чтобы я не хотел, чтобы в каждом методе тестирования не было сеанса или принципа, и я понимаю, что каждый метод тестирования должен быть чистым, чтобы предотвратить любое другое странное поведение. – Jun

ответ

0
// ignore spring security session 
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER); 
+3

Возможно, объясняя, где вы должны поставить этот код и объяснить, почему он должен решить проблему, ваш ответ будет лучше. –

+0

Вам нужно будет расширить WebSecurityConfigurerAdapter и поместить в него http.sessionManagement(). SessionCreationPolicy (SessionCreationPolicy.NEVER).Это происходит из аргумента, что службы останова являются безстоящими и сеанс имеет состояние, но поскольку это состояние, проведенное в сеансе, не связано с данными, а скорее сеанс - это метаданные, которые волнуют. – Pomagranite