«1. Обзор

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

В этом руководстве мы рассмотрим Spring Security с Auth0 с помощью пошагового руководства, а также основные настройки учетной записи Auth0.

2. Настройка Auth0

2.1. Регистрация Auth0

Во-первых, мы подпишемся на бесплатный план Auth0, который обеспечивает доступ до 7 тысяч активных пользователей с неограниченным количеством входов в систему. Однако мы можем пропустить этот раздел, если он у нас уже есть:

2.2. Панель инструментов

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

2.3. Создайте новое приложение

Затем в меню «Приложения» мы создадим новое приложение OpenID Connect (OIDC) для Spring Boot.

Далее мы выберем обычные веб-приложения в качестве типа приложения из доступных вариантов, таких как собственные, одностраничные приложения и межмашинные приложения:

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

Далее мы настроим несколько URI приложений, таких как URL-адреса обратного вызова и URL-адреса выхода, указывающие на наше приложение:

2.5. Учетные данные клиента

Наконец, мы получим значения домена, идентификатора клиента и секрета клиента, связанные с нашим приложением:

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

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

Теперь, когда наша учетная запись Auth0 готова с ключевыми конфигурациями, мы готовы интегрировать безопасность Auth0 в приложение Spring Boot.

3.1. Maven

Во-первых, давайте добавим последнюю зависимость mvc-auth-commons Maven в наш pom.xml:

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>mvc-auth-commons</artifactId>
    <version>1.2.0</version>
</dependency>

3.2. Gradle

Аналогично, при использовании Gradle мы можем добавить зависимость mvc-auth-commons в файл build.gradle:

compile 'com.auth0:mvc-auth-commons:1.2.0'

3.3. application.properties

Нашему приложению Spring Boot требуется такая информация, как идентификатор клиента и секрет клиента, чтобы включить аутентификацию учетной записи Auth0. Итак, добавим их в файл application.properties:

com.auth0.domain: dev-example.auth0.com
com.auth0.clientId: {clientId}
com.auth0.clientSecret: {clientSecret}

3.4. AuthConfig

Далее мы создадим класс AuthConfig для чтения свойств Auth0 из файла application.properties:

@Configuration
@EnableWebSecurity
public class AuthConfig extends WebSecurityConfigurerAdapter {
    @Value(value = "${com.auth0.domain}")
    private String domain;

    @Value(value = "${com.auth0.clientId}")
    private String clientId;

    @Value(value = "${com.auth0.clientSecret}")
    private String clientSecret;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http
          .authorizeRequests()
          .antMatchers("/callback", "/login", "/").permitAll()
          .anyRequest().authenticated()
          .and()
          .formLogin()
          .loginPage("/login")
          .and()
          .logout().logoutSuccessHandler(logoutSuccessHandler()).permitAll();
    }
}

Кроме того, класс AuthConfig настраивается для включения веб-безопасности путем расширения класса WebSecurityConfigurerAdapter.

3.5. AuthenticationController

Наконец, мы добавим ссылку на bean-компонент для класса AuthenticationController в уже обсуждавшийся класс AuthConfig:

@Bean
public AuthenticationController authenticationController() throws UnsupportedEncodingException {
    JwkProvider jwkProvider = new JwkProviderBuilder(domain).build();
    return AuthenticationController.newBuilder(domain, clientId, clientSecret)
      .withJwkProvider(jwkProvider)
      .build();
}

Здесь мы использовали класс JwkProviderBuilder при создании экземпляра класса AuthenticationController. Мы будем использовать это для получения открытого ключа для проверки подписи токена (по умолчанию токен подписывается с использованием алгоритма асимметричной подписи RS256).

Кроме того, bean-компонент authenticationController предоставляет URL-адрес авторизации для входа в систему и обрабатывает запрос обратного вызова.

4. AuthController

Далее мы создадим класс AuthController для функций входа и обратного вызова:

@Controller
public class AuthController {
    @Autowired
    private AuthConfig config;

    @Autowired 
    private AuthenticationController authenticationController;
}

Здесь мы внедрили зависимости классов AuthConfig и AuthenticationController, обсуждавшихся в предыдущем разделе.

4.1. Вход

Давайте создадим метод входа, который позволяет нашему приложению Spring Boot аутентифицировать пользователя:

@GetMapping(value = "/login")
protected void login(HttpServletRequest request, HttpServletResponse response) {
    String redirectUri = "http://localhost:8080/callback";
    String authorizeUrl = authenticationController.buildAuthorizeUrl(request, response, redirectUri)
      .withScope("openid email")
      .build();
    response.sendRedirect(authorizeUrl);
}

Метод buildAuthorizeUrl генерирует URL-адрес авторизации Auth0 и перенаправляет на экран входа Auth0 по умолчанию.

4.2. Обратный вызов

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

@GetMapping(value="/callback")
public void callback(HttpServletRequest request, HttpServletResponse response) {
    Tokens tokens = authenticationController.handle(request, response);
    
    DecodedJWT jwt = JWT.decode(tokens.getIdToken());
    TestingAuthenticationToken authToken2 = new TestingAuthenticationToken(jwt.getSubject(),
      jwt.getToken());
    authToken2.setAuthenticated(true);
    
    SecurityContextHolder.getContext().setAuthentication(authToken2);
    response.sendRedirect(config.getContextPath(request) + "/"); 
}

Мы обработали запрос обратного вызова, чтобы получить accessToken и idToken, которые представляют собой успешную аутентификацию. Затем мы создали объект TestingAuthenticationToken для установки аутентификации в SecurityContextHolder.

Однако мы можем создать нашу реализацию класса AbstractAuthenticationToken для большего удобства использования.

5. Домашний контроллер

«Наконец, мы создадим HomeController с сопоставлением по умолчанию для нашей целевой страницы приложения:

@Controller
public class HomeController {
    @GetMapping(value = "/")
    @ResponseBody
    public String home(final Authentication authentication) {
        TestingAuthenticationToken token = (TestingAuthenticationToken) authentication;
        DecodedJWT jwt = JWT.decode(token.getCredentials().toString());
        String email = jwt.getClaims().get("email").asString();
        return "Welcome, " + email + "!";
    }
}

Здесь мы извлекли объект DecodedJWT из idToken. Кроме того, информация о пользователе, такая как электронная почта, извлекается из заявок.

Вот оно! Наше приложение Spring Boot готово с поддержкой безопасности Auth0. Давайте запустим наше приложение с помощью команды Maven:

mvn spring-boot:run

При доступе к приложению по адресу localhost:8080/login мы увидим страницу входа по умолчанию, предоставленную Auth0:

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

Кроме того, мы найдем кнопку «Зарегистрироваться» (рядом с «Войти») на экране входа по умолчанию для самостоятельной регистрации. .

6. Регистрация

6.1. Самостоятельная регистрация

Впервые мы можем создать учетную запись Auth0, используя кнопку «Зарегистрироваться», а затем предоставив информацию, такую ​​как адрес электронной почты и пароль:

6.2. Создать пользователя

Или мы можем создать нового пользователя из меню «Пользователи» в учетной записи Auth0:

6.3. Настройки подключений

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

Кроме того, на выбор доступен ряд социальных подключений: ~ ~~ 7. LogoutController

Теперь, когда мы увидели функции входа и обратного вызова, мы можем добавить функцию выхода из системы в наше приложение Spring Boot.

Давайте создадим класс LogoutController, реализующий класс LogoutSuccessHandler:

Здесь метод onLogoutSuccess переопределен для вызова URL-адреса выхода /v2/logout Auth0.

@Controller
public class LogoutController implements LogoutSuccessHandler {
    @Autowired
    private AuthConfig config;

    @Override
    public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse res, 
      Authentication authentication) {
        if (req.getSession() != null) {
            req.getSession().invalidate();
        }
        String returnTo = "http://localhost:8080/";
        String logoutUrl = "https://dev-example.auth0.com/v2/logout?client_id=" +
          config.getClientId() + "&returnTo=" +returnTo;
        res.sendRedirect(logoutUrl);
    }
}

8. API управления Auth0

До сих пор мы видели интеграцию безопасности Auth0 в приложении Spring Boot. Теперь давайте взаимодействуем с API управления Auth0 (системный API) в том же приложении.

8.1. Создание нового приложения

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

8.2. Авторизация

Затем мы добавим авторизацию в Auth0 Management API с разрешениями на чтение/создание пользователей:

8.3. Учетные данные клиента

Наконец, мы получим идентификатор клиента и секрет клиента для доступа к приложению управления Auth0 из нашего приложения Spring Boot:

8.4. Токен доступа

Давайте создадим токен доступа для приложения управления Auth0, используя учетные данные клиента, полученные в предыдущем разделе:

Здесь мы сделали запрос REST к URL токена /oauth/token Auth0, чтобы получить токены доступа и обновления.

public String getManagementApiToken() {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

    JSONObject requestBody = new JSONObject();
    requestBody.put("client_id", "auth0ManagementAppClientId");
    requestBody.put("client_secret", "auth0ManagementAppClientSecret");
    requestBody.put("audience", "https://dev-example.auth0.com/api/v2/");
    requestBody.put("grant_type", "client_credentials"); 

    HttpEntity<String> request = new HttpEntity<String>(requestBody.toString(), headers);

    RestTemplate restTemplate = new RestTemplate();
    HashMap<String, String> result = restTemplate
      .postForObject("https://dev-example.auth0.com/oauth/token", request, HashMap.class);

    return result.get("access_token");
}

Кроме того, мы можем хранить эти учетные данные клиента в файле application.properties и читать их с помощью класса AuthConfig.

8.5. UserController

После этого давайте создадим класс UserController с методом пользователей:

Метод пользователей получает список всех пользователей, выполняя запрос GET к /api/v2/users Auth0 API с доступом токен, созданный в предыдущем разделе.

@Controller
public class UserController {
    @GetMapping(value="/users")
    @ResponseBody
    public ResponseEntity<String> users(HttpServletRequest request, HttpServletResponse response) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + getManagementApiToken());
        
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> result = restTemplate
          .exchange("https://dev-example.auth0.com/api/v2/users", HttpMethod.GET, entity, String.class);
        return result;
    }
}

Итак, давайте получим доступ к localhost:8080/users, чтобы получить ответ JSON, содержащий всех пользователей:

8.6. Создать пользователя

[{
    "created_at": "2020-05-05T14:38:18.955Z",
    "email": "[email protected]",
    "email_verified": true,
    "identities": [
        {
            "user_id": "5eb17a5a1cc1ac0c1487c37f78758",
            "provider": "auth0",
            "connection": "Username-Password-Authentication",
            "isSocial": false
        }
    ],
    "name": "[email protected]",
    "nickname": "ansh",
    "logins_count": 64
    // ...
}]

Точно так же мы можем создать пользователя, отправив запрос POST к /api/v2/users Auth0 API:

Затем давайте получим доступ к localhost:8080/createUser и проверим данные нового пользователя:

@GetMapping(value = "/createUser")
@ResponseBody
public ResponseEntity<String> createUser(HttpServletResponse response) {
    JSONObject request = new JSONObject();
    request.put("email", "[email protected]");
    request.put("given_name", "Norman");
    request.put("family_name", "Lewis");
    request.put("connection", "Username-Password-Authentication");
    request.put("password", "Pa33w0rd");
    
    // ...
    ResponseEntity<String> result = restTemplate
      .postForEntity("https://dev-example.auth0.com/api/v2/users", request.toString(), String.class);
    return result;
}

Точно так же мы можем выполнять различные операции, такие как вывод списка всех подключений, создание подключения, вывод списка всех клиентов и создание клиента с использованием API-интерфейсов Auth0, в зависимости от наших разрешений.

{
    "created_at": "2020-05-10T12:30:15.343Z",
    "email": "[email protected]",
    "email_verified": false,
    "family_name": "Lewis",
    "given_name": "Norman",
    "identities": [
        {
            "connection": "Username-Password-Authentication",
            "user_id": "5eb7f3d76b69bc0c120a8901576",
            "provider": "auth0",
            "isSocial": false
        }
    ],
    "name": "[email protected]",
    "nickname": "norman.lewis",
    // ...
}

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

В этом руководстве мы изучили Spring Security с Auth0.

Сначала мы настраиваем учетную запись Auth0 с необходимыми настройками. Затем мы создали приложение Spring Boot и настроили application.properties для интеграции Spring Security с Auth0.

Далее мы рассмотрели создание токена API для API управления Auth0. Наконец, мы рассмотрели такие функции, как получение всех пользователей и создание пользователя.

Как обычно, все реализации кода доступны на GitHub.

«