Рубрики
Без рубрики

Тестирование защищенного API OAuth с помощью Spring MVC (с использованием устаревшего стека Spring Security OAuth)

Краткое практическое руководство по тестированию API, защищенного OAuth, с поддержкой тестирования Spring MVC.

Автор оригинала: baeldung.

1. Обзор

В этой статье мы покажем, как мы можем протестировать API, защищенный с помощью OAuth с поддержкой тестирования Spring MVC .

Примечание : в этой статье используется устаревший проект Spring OAuth .

2. Сервер авторизации и ресурсов

Руководство по настройке сервера авторизации и ресурсов см. в предыдущей статье: Spring REST API + OAuth2 + AngularJS .

Наш сервер авторизации использует JdbcTokenStore и определяет клиента с идентификатором “foo ClientID Password” и паролем “secret” , а также поддерживает тип password grant.

Сервер ресурсов ограничивает URL-адрес /employee ролью АДМИНИСТРАТОРА.

Начиная с версии Spring Boot 1.5.0 адаптер безопасности имеет приоритет над адаптером ресурсов OAuth, поэтому, чтобы изменить порядок, мы должны аннотировать класс WebSecurityConfigurerAdapter с помощью @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) .

В противном случае Spring попытается получить доступ к запрошенным URL-адресам на основе правил безопасности Spring вместо правил Spring OAuth, и мы получим ошибку 403 при использовании проверки подлинности токенов.

3. Определение примера API

Во-первых, давайте создадим простое POJO под названием Employee с двумя свойствами, которыми мы будем управлять через API:

public class Employee {
    private String email;
    private String name;
    
    // standard constructor, getters, setters
}

Далее давайте определим контроллер с двумя сопоставлениями запросов для получения и сохранения объекта Employee в списке:

@Controller
public class EmployeeController {

    private List employees = new ArrayList<>();

    @GetMapping("/employee")
    @ResponseBody
    public Optional getEmployee(@RequestParam String email) {
        return employees.stream()
          .filter(x -> x.getEmail().equals(email)).findAny();
    }

    @PostMapping("/employee")
    @ResponseStatus(HttpStatus.CREATED)
    public void postMessage(@RequestBody Employee employee) {
        employees.add(employee);
    }
}

Имейте в виду, что для того, чтобы это сработало, нам нужен дополнительный модуль JDK8 Jackson . В противном случае Необязательный класс не будет сериализован/десериализован должным образом. Последнюю версию jackson-datatype-jdk8 можно загрузить с Maven Central.

4. Тестирование API

4.1. Настройка Тестового класса

Чтобы протестировать наш API, мы создадим тестовый класс с аннотацией @SpringBootTest , который использует класс AuthorizationServerApplication для чтения конфигурации приложения.

Для тестирования защищенного API с поддержкой тестов Spring MVC нам необходимо ввести Webapplicationcontext и Цепочку фильтров безопасности Spring beans. Мы будем использовать их для получения экземпляра MockMvc перед запуском тестов:

@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = AuthorizationServerApplication.class)
public class OAuthMvcTest {

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    private FilterChainProxy springSecurityFilterChain;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
          .addFilter(springSecurityFilterChain).build();
    }
}

4.2. Получение токена доступа

Проще говоря, API, защищенный с помощью OAuth2 ожидает получения заголовка Authorization | со значением Bearer .

Чтобы отправить требуемый заголовок Authorization , нам сначала нужно получить действительный токен доступа, отправив запрос POST в конечную точку /oauth/token|/. Эта конечная точка требует базовой аутентификации HTTP с идентификатором и секретом клиента OAuth, а также списком параметров, указывающих client_id , grant_type , имя пользователя и пароль .

Используя поддержку тестов Spring MVC, параметры могут быть завернуты в многозначную карту , а аутентификация клиента может быть отправлена с помощью метода httpBasic .

Давайте создадим метод, который отправляет запрос POST для получения токена и считывает значение access_token из ответа JSON:

private String obtainAccessToken(String username, String password) throws Exception {
 
    MultiValueMap params = new LinkedMultiValueMap<>();
    params.add("grant_type", "password");
    params.add("client_id", "fooClientIdPassword");
    params.add("username", username);
    params.add("password", password);

    ResultActions result 
      = mockMvc.perform(post("/oauth/token")
        .params(params)
        .with(httpBasic("fooClientIdPassword","secret"))
        .accept("application/json;charset=UTF-8"))
        .andExpect(status().isOk())
        .andExpect(content().contentType("application/json;charset=UTF-8"));

    String resultString = result.andReturn().getResponse().getContentAsString();

    JacksonJsonParser jsonParser = new JacksonJsonParser();
    return jsonParser.parseMap(resultString).get("access_token").toString();
}

4.3. Тестирование запросов на ПОЛУЧЕНИЕ и отправку

Маркер доступа может быть добавлен в запрос с помощью метода header(“Authorization”, “Bearer “+ accessToken) .

Давайте попытаемся получить доступ к одному из наших защищенных сопоставлений без заголовка Authorization и убедимся, что мы получили несанкционированный код состояния:

@Test
public void givenNoToken_whenGetSecureRequest_thenUnauthorized() throws Exception {
    mockMvc.perform(get("/employee")
      .param("email", EMAIL))
      .andExpect(status().isUnauthorized());
}

Мы указали, что только пользователи с ролью АДМИНИСТРАТОРА могут получить доступ к URL-адресу /employee . Давайте создадим тест, в котором мы получим маркер доступа для пользователя с ролью USER и проверим, что мы получили код состояния forbidden :

@Test
public void givenInvalidRole_whenGetSecureRequest_thenForbidden() throws Exception {
    String accessToken = obtainAccessToken("user1", "pass");
    mockMvc.perform(get("/employee")
      .header("Authorization", "Bearer " + accessToken)
      .param("email", "[email protected]"))
      .andExpect(status().isForbidden());
}

Затем давайте протестируем наш API с использованием допустимого маркера доступа, отправив запрос POST для создания объекта Employee , а затем запрос GET для чтения созданного объекта:

@Test
public void givenToken_whenPostGetSecureRequest_thenOk() throws Exception {
    String accessToken = obtainAccessToken("admin", "nimda");

    String employeeString = "{\"email\":\"[email protected]\",\"name\":\"Jim\"}";
        
    mockMvc.perform(post("/employee")
      .header("Authorization", "Bearer " + accessToken)
      .contentType(application/json;charset=UTF-8)
      .content(employeeString)
      .accept(application/json;charset=UTF-8))
      .andExpect(status().isCreated());

    mockMvc.perform(get("/employee")
      .param("email", "[email protected]")
      .header("Authorization", "Bearer " + accessToken)
      .accept("application/json;charset=UTF-8"))
      .andExpect(status().isOk())
      .andExpect(content().contentType(application/json;charset=UTF-8))
      .andExpect(jsonPath("$.name", is("Jim")));
}

5. Заключение

В этом кратком руководстве мы продемонстрировали, как мы можем протестировать API, защищенный OAuth, с помощью поддержки тестов Spring MVC.

Полный исходный код примеров можно найти в проекте GitHub .

Для запуска теста в проекте есть профиль mvc , который можно выполнить с помощью команды mvn clean install -Pvc.