Статьи

Краткое руководство по OAuth 2.0 с Spring Security

«Я люблю писать код аутентификации и авторизации». Нет Java-разработчика. Надоело строить одни и те же экраны входа снова и снова? Попробуйте API Okta для размещенной аутентификации, авторизации и многофакторной аутентификации.

При создании веб-приложения авторизация и авторизация являются обязательными. Однако сделать это правильно не просто. Компьютерная безопасность — это настоящая специальность. Легионы разработчиков работают днем ​​и ночью против одинаково многочисленных международных хакеров, создавая непрерывный цикл разработки для поиска уязвимостей, их атаки и исправления. Следить за всем этим соло было бы больно (если не невозможно).

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

В этом руководстве вы сначала создадите веб-приложение OAuth 2.0 и сервер аутентификации, используя Spring Boot и Spring Security. После этого вы будете использовать Okta, чтобы избавиться от собственного сервера аутентификации и еще больше упростить приложение Spring Boot.

Давайте начнем!

Создайте сервер OAuth 2.0

Начните с перехода к Spring Initializr и создания нового проекта со следующими настройками:

  • Измените тип проекта с Maven на Gradle .
  • Измените группу на com.okta.spring .
  • Измените артефакт на AuthorizationServerApplication .
  • Добавьте одну зависимость: Web .
Spring Security

Скачайте проект и скопируйте его куда-нибудь на свой жесткий диск. В этом уроке вы создадите три разных проекта, так что вы можете захотеть создать родительский каталог, где-то вроде SpringBootOAuth .

Вам нужно добавить одну зависимость в файл build.gradle :

implementation 'org.springframework.security.oauth:spring-security-oauth2:2.3.3.RELEASE'

Это добавляет OAuth совершенства Spring.

Обновите src/main/resources/application.properties чтобы он соответствовал:

server.port=8081
server.servlet.context-path=/auth
user.oauth.clientId=R2dpxQ3vPrtfgF72
user.oauth.clientSecret=fDw7Mpkk5czHNuSRtmhGmAGL42CaxQB9
user.oauth.redirectUris=http://localhost:8082/login/oauth2/code/
user.oauth.user.username=Andrew
user.oauth.user.password=abcd

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

Обновите класс AuthorizationServerApplication добавив @EnableResourceServer :

package com.okta.spring.AuthorizationServerApplication;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
 
@SpringBootApplication
@EnableResourceServer
public class AuthorizationServerApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(AuthorizationServerApplication.class, args);
    }
}

Создайте новый класс AuthServerConfig в том же пакете, что и ваш класс приложения com.okta.spring.AuthorizationServerApplication в src/main/java (отныне создавайте Java-классы в src/main/java/com/okta/spring/AuthorizationServerApplication ). Этот класс конфигурации Spring включает и настраивает сервер авторизации OAuth.

package com.okta.spring.AuthorizationServerApplication;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
 
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
 
    @Value("${user.oauth.clientId}")
    private String ClientID;
    @Value("${user.oauth.clientSecret}")
    private String ClientSecret;
    @Value("${user.oauth.redirectUris}")
    private String RedirectURLs;
 
   private final PasswordEncoder passwordEncoder;
     
    public AuthServerConfig(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }
 
    @Override
    public void configure(
        AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()");
    }
 
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient(ClientID)
            .secret(passwordEncoder.encode(ClientSecret))
            .authorizedGrantTypes("authorization_code")
            .scopes("user_info")
            .autoApprove(true)
            .redirectUris(RedirectURLs);
    }
}

Класс AuthServerConfig — это класс, который создает и возвращает наши веб-токены JSON при правильной аутентификации клиента.

Создайте класс SecurityConfiguration :

package com.okta.spring.AuthorizationServerApplication;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 
@Configuration
@Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
 
    @Value("${user.oauth.user.username}")
    private String username;
    @Value("${user.oauth.user.password}")
    private String password;
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
            .antMatchers("/login", "/oauth/authorize")
            .and()
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin().permitAll();
    }
 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser(username)
            .password(passwordEncoder().encode(password))
            .roles("USER");
    }
 
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Класс SecurityConfiguration — это класс, который фактически проверяет подлинность запросов к вашему серверу авторизации. Обратите внимание на верхнюю часть, где он извлекает имя пользователя и пароль из файла application.properties .

Наконец, создайте класс Java с именем UserController :

package com.okta.spring.AuthorizationServerApplication;
 
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.security.Principal;
 
@RestController
public class UserController {
 
    @GetMapping("/user/me")
    public Principal user(Principal principal) {
        return principal;
    }
}

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

Это ваш ресурсный сервер! Не так уж плохо. Spring Boot делает это довольно легко. Четыре файла и несколько свойств. Чуть позже вы сделаете это еще проще с Okta, но сейчас перейдем к созданию клиентского приложения, которое вы можете использовать для тестирования сервера аутентификации.

Запустите сервер авторизации:

./gradlew bootRun

Подождите немного, пока он закончит работу. Терминал должен заканчиваться примерно так:

...
2019-02-23 19:06:49.122  INFO 54333 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path '/auth  '
2019-02-23 19:06:49.128  INFO 54333 --- [           main] c.o.s.A.AuthorizationServerApplication   : Started AuthorizationServerApplication in 3.502 seconds (JVM running for 3.945)

ПРИМЕЧАНИЕ. Если вы получаете сообщение об ошибке JAXB ( java.lang.ClassNotFoundException: javax.xml.bind.JAXBException ), то это потому, что вы используете Java 11. Чтобы это исправить, добавьте JAXB в свой build.gradle .

implementation 'org.glassfish.jaxb:jaxb-runtime'

Создайте свое клиентское приложение

Вернуться к Весне Инициализр . Создайте новый проект со следующими настройками:

  • Тип проекта должен быть Gradle (не Maven).
  • Группа: com.okta.spring .
  • Артефакт: SpringBootOAuthClient .
  • Добавьте три зависимости: Web , Thymeleaf , OAuth2 Client .
Spring Security

Загрузите проект, скопируйте его в место последнего распаковывания и распакуйте его.

На этот раз вам нужно добавить следующую зависимость в ваш файл build.gradle :

implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE'

Переименуйте src/main/resources/application.properties в application.yml и обновите его, чтобы он соответствовал YAML ниже:

server:
  port: 8082
  session:
    cookie:
      name: UISESSION
spring:
  thymeleaf:
    cache: false
  security:
    oauth2:
      client:
        registration:
          custom-client:
            client-id: R2dpxQ3vPrtfgF72
            client-secret: fDw7Mpkk5czHNuSRtmhGmAGL42CaxQB9
            client-name: Auth Server
            scope: user_info
            provider: custom-provider
            redirect-uri-template: http://localhost:8082/login/oauth2/code/
            client-authentication-method: basic
            authorization-grant-type: authorization_code
        provider:
          custom-provider:
            token-uri: http://localhost:8081/auth/oauth/token
            authorization-uri: http://localhost:8081/auth/oauth/authorize
            user-info-uri: http://localhost:8081/auth/user/me
            user-name-attribute: name

Обратите внимание, что здесь вы настраиваете clientId и clientSecret , а также различные URI для вашего сервера аутентификации. Они должны соответствовать значениям в другом проекте.

Обновите класс SpringBootOAuthClientApplication чтобы он соответствовал:

package com.okta.spring.SpringBootOAuthClient;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class SpringBootOAuthClientApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringBootOAuthClientApplication.class, args);
    }
}

Создайте новый класс Java с именем WebController :

package com.okta.spring.SpringBootOAuthClient;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
 
import java.security.Principal;
 
@Controller
public class WebController {
 
    @RequestMapping("/securedPage")
    public String securedPage(Model model, Principal principal) {
        return "securedPage";
    }
 
    @RequestMapping("/")
    public String index(Model model, Principal principal) {
        return "index";
    }
}

Это контроллер, который отображает входящие запросы в ваши файлы шаблонов Thymeleaf (которые вы сделаете через секунду).

Создайте другой класс Java с именем SecurityConfiguration :

package com.okta.spring.SpringBootOAuthClient;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**").authorizeRequests()
            .antMatchers("/", "/login**").permitAll()
            .anyRequest().authenticated()
            .and()
            .oauth2Login();
    }
}

Этот класс определяет конфигурацию Spring Security для вашего приложения: разрешает все запросы на домашнем пути и требует аутентификации для всех других маршрутов. он также устанавливает поток входа в Spring Boot OAuth.

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

Шаблоны находятся в каталоге src/main/resources/templates . Вы заметите в контроллере выше, что они просто возвращают строки для маршрутов. Когда в сборку включены зависимости Thymeleaf, Spring Boot автоматически предполагает, что вы возвращаете имя файла шаблона из контроллеров, и поэтому приложение будет искать в src/main/resources/templates имя файла с возвращенной строкой плюс .html .

Создайте домашний шаблон: src/main/resources/templates/index.html :

<!DOCTYPE html> 
<html lang="en"
<head
    <meta charset="UTF-8"
    <title>Home</title
</head
<body
    <h1>Spring Security SSO</h1
    <a href="securedPage">Login</a
</body
</html>

И защищенный шаблон: src/main/resources/templates/securedPage.html :

<!DOCTYPE html> 
<html xmlns:th="http://www.thymeleaf.org"
<head
    <meta charset="UTF-8"
    <title>Secured Page</title
</head
<body
    <h1>Secured Page</h1
    <span th:text="${#authentication.name}"></span
</body
</html>

Я просто укажу на одну строку:

<span th:text="${#authentication.name}"></span>

Это строка, которая будет вставлять имя аутентифицированного пользователя. В этой строке вам понадобилась org.thymeleaf.extras:thymeleaf-extras-springsecurity5 в файле build.gradle .

Запустите клиентское приложение:

./gradlew bootRun

Подожди, пока оно закончится. Терминал должен заканчиваться примерно так:

...
2019-02-23 19:29:04.448  INFO 54893 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8082 (http) with context path ''
2019-02-23 19:29:04.453  INFO 54893 --- [           main] c.o.s.S.SpringBootOAuthClientApplication : Started SpringBootOAuthClientApplication in 3.911 seconds (JVM running for 4.403)

Протестируйте сервер ресурсов

Перейдите в выбранном браузере к клиентскому приложению по адресу http://localhost:8082/ .

Нажмите на ссылку Войти .

Вы будете перенаправлены на страницу входа:

Spring Security

Введите имя пользователя Andrew и пароль abcd (из файла application.properties с сервера аутентификации).

Нажмите « Войти», и вы попадете в супер-необычный шаблон securedPage.html с надписью «Защищенная страница» и «Эндрю».

Большой! Оно работает. Теперь ты собираешься сделать это еще проще.

Вы можете остановить как серверные, так и клиентские приложения Spring Boot.

Создать приложение OpenID Connect

Okta является провайдером аутентификации и авторизации SaaS (программное обеспечение как услуга). Мы предоставляем бесплатные аккаунты разработчикам, чтобы они могли без проблем создавать приложения OIDC. Зайдите на сайт developer.okta.com и зарегистрируйтесь. После того как вы подтвердили свою электронную почту, войдите в систему и выполните следующие действия:

  • Перейдите в Приложение > Добавить приложение .
  • Выберите тип приложения Web и нажмите Next .
  • Дайте приложению имя. Я назвал мой «Spring Boot OAuth».
  • В разделе URI перенаправления входа в систему измените значение на http://localhost:8080/login/oauth2/code/okta . Остальные значения по умолчанию будут работать.
  • Нажмите Готово .

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

Создать новое приложение Spring Boot

Вернемся к Spring Initializr еще раз. Создайте новый проект со следующими настройками:

  • Измените тип проекта с Maven на Gradle .
  • Измените группу на com.okta.spring .
  • Измените Артефакт на OktaOAuthClient .
  • Добавьте три зависимости: Web , Thymeleaf , Okta .
  • Нажмите Создать проект .
Spring Security

Скопируйте проект и распакуйте его куда-нибудь.

В файле build.gradle добавьте следующую зависимость:

implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE'

Также, пока вы там, обратите внимание на зависимость com.okta.spring:okta-spring-boot-starter:1.1.0 . Это Okta Spring Boot Starter. Это удобный проект, который делает интеграцию Okta с Spring Boot приятной и простой. Для получения дополнительной информации взгляните на GitHub проекта .

Измените src/main/resources/application.properties на application.yml и добавьте следующее:

server:
  port: 8080
okta:
  oauth2:
    issuer: https://{yourOktaDomain}/oauth2/default
    client-id: {yourClientId}
    client-secret: {yourClientSecret}
spring:
  thymeleaf:
    cache: false

Помните, когда я сказал, что вам понадобится ваш ClientID и Client Secret выше. Ну, время пришло. Вы должны заполнить их в файл, а также ваш URL издателя Okta. Это будет выглядеть примерно так: dev-123456.okta.com . Вы можете найти его в разделе API > Серверы авторизации .

Вам также понадобятся два похожих файла шаблона в каталоге src/main/resources/templates . Файл шаблона index.html точно такой же, и его можно скопировать, если хотите. securedPage.html шаблона securedPage.html немного отличается из-за способа, которым информация об аутентификации возвращается из Okta по сравнению с простым сервером аутентификации, который вы создали ранее.

Создайте домашний шаблон: src/main/resources/templates/index.html :

<!DOCTYPE html> 
<html lang="en"
<head
    <meta charset="UTF-8"
    <title>Home</title
</head
<body
    <h1>Spring Security SSO</h1
    <a href="securedPage">Login</a
</body
</html>

И защищенный шаблон: src/main/resources/templates/securedPage.html :

<!DOCTYPE html> 
<html xmlns:th="http://www.thymeleaf.org"
<head
    <meta charset="UTF-8"
    <title>Secured Page</title
</head
<body
    <h1>Secured Page</h1
    <span th:text="${#authentication.principal.attributes.name}">Joe Coder</span
</body
</html>

Создайте класс Java с именем WebController в пакете com.okta.spring.SpringBootOAuth :

package com.okta.spring.OktaOAuthClient;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
 
import java.security.Principal;
 
@Controller
public class WebController {
     
    @RequestMapping("/securedPage")
    public String securedPage(Model model, Principal principal) {
        return "securedPage";
    }
     
    @RequestMapping("/")
    public String index(Model model, Principal principal) {
        return "index";
    }
}

Этот класс просто создает два маршрута, один для домашнего маршрута и один для защищенного маршрута. Опять же, Spring Boot и Thymeleaf автоматически маскируют это для двух файлов шаблонов в src/main/resources/templates .

Наконец, создайте еще один Java-класс имен SecurityConfiguration :

package com.okta.spring.OktaOAuthClient;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**").authorizeRequests()
            .antMatchers("/").permitAll()
            .anyRequest().authenticated()
            .and()
            .oauth2Login();
    }
}

Это оно! Бам!

Запустите клиент с поддержкой Okta-OAuth:

./gradlew bootRun

Вы должны увидеть кучу выходных данных, которые заканчиваются на:

...
2019-02-23 20:09:03.465  INFO 55890 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-02-23 20:09:03.470  INFO 55890 --- [           main] c.o.s.O.OktaOAuthClientApplication       : Started OktaOAuthClientApplication in 3.285 seconds (JVM running for 3.744)

Перейдите по адресу http: // localhost: 8080 .

Нажмите кнопку Войти .

На этот раз вы будете перенаправлены на страницу входа в Okta. Вам может понадобиться использовать браузер в режиме инкогнито или выйти из своей панели мониторинга developer.okta.com, чтобы не пропустить страницу входа и не получить немедленный доступ к защищенной конечной точке.

Spring Security

Авторизуйтесь, и вы увидите защищенную страницу с вашим именем!

Узнайте больше о Spring Boot, Spring Security и OAuth 2.0

Вот и все. Супер просто. В предыдущем руководстве вы рассмотрели, как использовать Spring Boot и Spring Security для реализации базового сервера аутентификации и клиентского приложения. Затем вы использовали Okta, чтобы сделать еще более простое клиентское приложение с полнофункциональной аутентификацией SSO и OAuth.

Вы можете увидеть готовый код этого руководства на GitHub по адресу oktadeveloper / okta-spring-boot-authz-server-example .

Если вы хотите узнать больше о Spring Boot, OAuth 2.0 и Spring Security, ознакомьтесь с этими полезными руководствами:

Если у вас есть какие-либо вопросы по поводу этого поста, пожалуйста, добавьте комментарий ниже. Чтобы получить более интересный контент, следите за @oktadev в Twitter или подпишитесь на наш канал на YouTube !

«Краткое руководство по OAuth 2.0 с Spring Security» первоначально было опубликовано в блоге разработчиков Okta в марте 2019 года.

«Я люблю писать код аутентификации и авторизации». Нет Java-разработчика. Надоело строить одни и те же экраны входа снова и снова? Попробуйте API Okta для размещенной аутентификации, авторизации и многофакторной аутентификации.