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

Социальный вход с помощью Spring Security в приложении Джерси

Узнайте, как защитить приложение Spring Boot Jersey с помощью Spring Social login и OAuth2.

Автор оригинала: Nguyen Nam Thai.

1. Обзор

Безопасность-это первоклассный гражданин в экосистеме Spring. Поэтому неудивительно, что OAuth2 может работать с Spring Web MVC практически без конфигурации.

Однако собственное решение Spring-не единственный способ реализации уровня представления. Jersey , реализация, совместимая с JAX-RS, также может работать в тандеме с Spring OAuth2.

В этом уроке мы узнаем, как защитить приложение Jersey с помощью Spring Social Login, которое реализовано с использованием стандарта OAuth2.

2. Зависимости Maven

Давайте добавим spring-boot-starter-jersey артефакт для интеграции Jersey в приложение Spring Boot:


    org.springframework.boot
    spring-boot-starter-jersey

Для настройки безопасности OAuth2 нам нужны spring-boot-starter-security и spring-security-oauth2-client :


    org.springframework.boot
    spring-boot-starter-security


    org.springframework.security
    spring-security-oauth2-client

Мы будем управлять всеми этими зависимостями с помощью родительской версии Spring Boot Starter 2 .

3. Слой презентации Джерси

Нам понадобится класс ресурсов с несколькими конечными точками, чтобы использовать Джерси в качестве слоя представления.

3.1. Класс ресурсов

Вот класс, содержащий определения конечных точек:

@Path("/")
public class JerseyResource {
    // endpoint definitions
}

Сам класс очень прост – он имеет только аннотацию @Path . Значение этой аннотации определяет базовый путь для всех конечных точек в теле класса.

Возможно, стоит упомянуть, что этот класс ресурсов не содержит аннотации стереотипа для сканирования компонентов. На самом деле, это даже не обязательно должен быть весенний боб. Причина в том, что мы не полагаемся на Spring для обработки сопоставления запросов.

3.2. Страница входа в систему

Вот метод, который обрабатывает запросы на вход в систему:

@GET
@Path("login")
@Produces(MediaType.TEXT_HTML)
public String login() {
    return "Log in with GitHub";
}

Этот метод возвращает строку для запросов GET, нацеленных на конечную точку /login . Тип содержимого text/html указывает браузеру пользователя отображать ответ с кликабельной ссылкой.

Мы будем использовать GitHub в качестве поставщика OAuth2, отсюда ссылка /oauth2/authorization/github . Эта ссылка вызовет перенаправление на страницу авторизации GitHub.

3.3. Домашняя страница

Давайте определим другой метод обработки запросов к корневому пути:

@GET
@Produces(MediaType.TEXT_PLAIN)
public String home(@Context SecurityContext securityContext) {
    OAuth2AuthenticationToken authenticationToken = (OAuth2AuthenticationToken) securityContext.getUserPrincipal();
    OAuth2AuthenticatedPrincipal authenticatedPrincipal = authenticationToken.getPrincipal();
    String userName = authenticatedPrincipal.getAttribute("login");
    return "Hello " + userName;
}

Этот метод возвращает домашнюю страницу, которая представляет собой строку, содержащую имя пользователя, вошедшего в систему. Обратите внимание, что в этом случае мы извлекли имя пользователя из атрибута login . Другой поставщик OAuth2 может использовать другой атрибут для имени пользователя.

Очевидно, что приведенный выше метод работает только для аутентифицированных запросов. Если запрос не прошел проверку подлинности, он будет перенаправлен на конечную точку login . Мы увидим, как настроить это перенаправление в разделе 4.

3.4. Регистрация Джерси с Пружинным контейнером

Давайте зарегистрируем класс ресурсов в контейнере сервлетов, чтобы включить службы Jersey. К счастью, это довольно просто:

@Component
public class RestConfig extends ResourceConfig {
    public RestConfig() {
        register(JerseyResource.class);
    }
}

Зарегистрировав Ресурс Джерси в подклассе ResourceConfig , мы проинформировали контейнер сервлета обо всех конечных точках в этом классе ресурсов.

Последним шагом является регистрация подкласса ResourceConfig , который в данном случае является RestConfig , с контейнером Spring. Мы реализовали эту регистрацию с помощью аннотации @Component|/.

4. Настройка безопасности Spring

Мы можем настроить безопасность для Джерси так же, как для обычного приложения Spring:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
          .authorizeRequests()
          .antMatchers("/login")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .oauth2Login()
          .loginPage("/login");
    }
}

Наиболее важным методом в данной цепочке является oauth2Login . Этот метод настраивает поддержку аутентификации с помощью поставщика OAuth 2.0. В этом учебнике поставщиком является GitHub.

Еще одна заметная конфигурация-это страница входа в систему. Предоставляя сильный “/login” методу LoginPage , мы сообщаем Spring перенаправлять не прошедшие проверку подлинности запросы на конечную точку /login .

Обратите внимание, что конфигурация безопасности по умолчанию также предоставляет автоматически созданную страницу в /login . Поэтому, даже если мы не настроили страницу входа в систему, и не прошедший проверку подлинности запрос все равно будет перенаправлен на эту конечную точку.

Разница между конфигурацией по умолчанию и явной настройкой заключается в том, что в случае по умолчанию приложение возвращает сгенерированную страницу, а не нашу пользовательскую строку.

5. Конфигурация приложения

Чтобы иметь приложение, защищенное OAuth2, нам нужно будет зарегистрировать клиента у поставщика OAuth2. После этого добавьте учетные данные клиента в приложение.

5.1. Регистрация Клиента OAuth2

Давайте начнем процесс регистрации с регистрации приложения GitHub . После перехода на страницу разработчика GitHub нажмите кнопку Новое приложение OAuth , чтобы открыть форму Регистрация нового приложения OAuth .

Затем заполните отображаемую форму соответствующими значениями. Для имени приложения введите любую строку, которая делает приложение узнаваемым. URL-адрес домашней страницы может быть http://localhost:8083, и URL-адрес обратного вызова авторизации: http://localhost:8083/login/oauth2/code/github .

URL-адрес обратного вызова-это путь, на который перенаправляется браузер после аутентификации пользователя с помощью GitHub и предоставления доступа к приложению.

Вот как может выглядеть регистрационная форма:

Теперь нажмите на кнопку Зарегистрировать приложение . Затем браузер должен перенаправить на домашнюю страницу приложения GitHub, на которой отображается идентификатор клиента и секрет клиента.

5.2. Настройка приложения Spring Boot

Давайте добавим файл свойств с именем jersey-application.properties в путь к классу:

server.port=8083
spring.security.oauth2.client.registration.github.client-id=
spring.security.oauth2.client.registration.github.client-secret=

Не забудьте заменить заполнители <ваш-идентификатор клиента> и <секрет вашего клиента> со значениями из нашего собственного приложения GitHub.

Наконец, добавьте этот файл в качестве источника свойств в приложение Spring Boot:

@SpringBootApplication
@PropertySource("classpath:jersey-application.properties")
public class JerseyApplication {
    public static void main(String[] args) {
        SpringApplication.run(JerseyApplication.class, args);
    }
}

6. Аутентификация в действии

Давайте посмотрим, как мы можем войти в ваше приложение после регистрации на GitHub.

6.1. Доступ к Приложению

Давайте запустим приложение, а затем перейдем на домашнюю страницу по адресу localhost:8083 . Поскольку запрос не прошел проверку подлинности, мы будем перенаправлены на страницу login :

Теперь, когда мы перейдем по ссылке GitHub, браузер перенаправит на страницу авторизации GitHub:

Посмотрев на URL-адрес, мы видим, что перенаправленный запрос содержал множество параметров запроса, таких как response_type , client_id и scope :

https://github.com/login/oauth/authorize?response_type=code&client_id=c30a16c45a9640771af5&scope=read:user&state=dpTme3pB87wA7AZ--XfVRWSkuHD3WIc9Pvn17yeqw38%3D&redirect_uri=http://localhost:8083/login/oauth2/code/github

Значение response_type равно code , что означает, что тип гранта OAuth2-это код авторизации. Между тем, параметр client_id помогает идентифицировать наше приложение. Для получения значений всех параметров, пожалуйста, перейдите на страницу разработчика GitHub .

Когда появится страница авторизации, нам нужно авторизовать приложение для продолжения работы. После успешной авторизации браузер перенаправится на предопределенную конечную точку в нашем приложении вместе с несколькими параметрами запроса:

http://localhost:8083/login/oauth2/code/github?code=561d99681feeb5d2edd7&state=dpTme3pB87wA7AZ--XfVRWSkuHD3WIc9Pvn17yeqw38%3D

За кулисами приложение затем обменяет код авторизации на маркер доступа. После этого он использует этот токен для получения информации о вошедшем в систему пользователе.

После возврата запроса на localhost:8083/login/oauth2/code/github браузер возвращается на главную страницу. На этот раз мы должны увидеть поздравительное сообщение с вашим собственным именем пользователя :

6.2. Как получить Имя пользователя?

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

В нашем примере мы извлекли имя пользователя из атрибута login . Однако это не то же самое для всех поставщиков OAuth2. Другими словами, поставщик может предоставлять данные в определенных атрибутах по своему усмотрению. Поэтому мы можем сказать, что в этом отношении просто нет стандартов.

В случае с GitHub мы можем найти необходимые атрибуты в справочной документации . Аналогичным образом, другие поставщики OAuth2 предоставляют свои собственные ссылки.

Другое решение заключается в том, что мы можем запустить приложение в режиме отладки и установить точку останова после OAuth2AuthenticatedPrincipal объект создан. При просмотре всех атрибутов этого объекта мы получим представление о пользовательской информации.

7. Тестирование

Давайте напишем несколько тестов, чтобы проверить поведение приложения.

7.1. Настройка Среды

Вот класс, который будет содержать наши методы тестирования:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@TestPropertySource(properties = "spring.security.oauth2.client.registration.github.client-id:test-id")
public class JerseyResourceUnitTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @LocalServerPort
    private int port;

    private String basePath;

    @Before
    public void setup() {
        basePath = "http://localhost:" + port + "/";
    }

    // test methods
}

Вместо использования реального идентификатора клиента GitHub мы определили тестовый идентификатор для клиента OAuth2. Этот идентификатор затем отправляется в свойство spring.security.oauth2.client.registration.github.client-id|/.

Все аннотации в этом тестовом классе являются общими для весеннего тестирования загрузки, поэтому мы не будем рассматривать их в этом руководстве. В случае, если какая-либо из этих аннотаций неясна , пожалуйста , перейдите к тестированию в Spring Boot, интеграционному тестированию в Spring или изучите шаблон Spring Boot TestRestTemplate .

7.2. Домашняя страница

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

@Test
public void whenUserIsUnauthenticated_thenTheyAreRedirectedToLoginPage() {
    ResponseEntity response = restTemplate.getForEntity(basePath, Object.class);
    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FOUND);
    assertThat(response.getBody()).isNull();

    URI redirectLocation = response.getHeaders().getLocation();
    assertThat(redirectLocation).isNotNull();
    assertThat(redirectLocation.toString()).isEqualTo(basePath + "login");
}

7.3. Страница входа в систему

Давайте проверим, что доступ к странице входа приведет к возвращению пути авторизации:

@Test
public void whenUserAttemptsToLogin_thenAuthorizationPathIsReturned() {
    ResponseEntity response = restTemplate.getForEntity(basePath + "login", String.class);
    assertThat(response.getHeaders().getContentType()).isEqualTo(TEXT_HTML);
    assertThat(response.getBody()).isEqualTo("Log in with GitHub");
}

7.4. Конечная точка авторизации

Наконец, при отправке запроса на конечную точку авторизации браузер перенаправит на страницу авторизации поставщика OAuth2 с соответствующими параметрами:

@Test
public void whenUserAccessesAuthorizationEndpoint_thenTheyAresRedirectedToProvider() {
    ResponseEntity response = restTemplate.getForEntity(basePath + "oauth2/authorization/github", String.class);
    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FOUND);
    assertThat(response.getBody()).isNull();

    URI redirectLocation = response.getHeaders().getLocation();
    assertThat(redirectLocation).isNotNull();
    assertThat(redirectLocation.getHost()).isEqualTo("github.com");
    assertThat(redirectLocation.getPath()).isEqualTo("/login/oauth/authorize");

    String redirectionQuery = redirectLocation.getQuery();
    assertThat(redirectionQuery.contains("response_type=code"));
    assertThat(redirectionQuery.contains("client_id=test-id"));
    assertThat(redirectionQuery.contains("scope=read:user"));
}

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

В этом уроке мы настроили Spring Social Login с помощью приложения Jersey. Учебник также включал шаги по регистрации приложения у поставщика GitHub OAuth2.

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