Spring Boot+OAuth2, единая аннотация для единого входа!

Spring Boot
Spring Boot+OAuth2, единая аннотация для единого входа!

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

Весенняя серия безопасности:

  1. Выкопайте большую яму и позвольте Spring Security сделать это!
  2. Song Ge поможет вам начать работу с Spring Security, не спрашивайте, как снова расшифровать пароль.
  3. Научите, как настроить вход в форму в Spring Security
  4. Spring Security разделяет внешний и внутренний интерфейсы, давайте не будем переходить по страницам! Все взаимодействия JSON
  5. Операции авторизации в Spring Security оказались такими простыми
  6. Как Spring Security хранит пользовательские данные в базе данных?
  7. Spring Security+Spring Data Jpa объединяют усилия, управление безопасностью становится еще проще!

Серия OAuth2:

  1. Чтобы сделать OAuth2, который нельзя обойти микросервисами, Сонг Гэ также пришел поговорить с вами об этом.
  2. Этот случай написан, и вы боитесь, что не сможете понять процесс входа в систему OAuth2 с интервьюером?
  3. Я умираю от OAuth2, я хочу изучить полный набор тренеров!
  4. Можно ли хранить токены OAuth2 в Redis? Играйте больше и больше!
  5. Хотите весело провести время, играя вместе с OAuth2 и JWT? Пожалуйста, посмотрите выступление Сон Гэ
  6. Поделитесь с вами некоторыми идеями по управлению безопасностью в микросервисной архитектуре.

Хорошо, давайте начнем сегодняшний текст.

Единый вход — это обычное требование в распределенных системах.

Распределенная система состоит из множества разных подсистем, и когда мы используем систему, нам нужно войти в систему только один раз, чтобы другие системы думали, что пользователь уже вошел в систему, и нет необходимости входить снова. Ранее я поделился с друзьями методом входа в систему OAuth2+JWT. Этот вход без сохранения состояния фактически соответствует потребностям единого входа. Вы можете обратиться к:Хотите весело провести время, играя вместе с OAuth2 и JWT? Пожалуйста, посмотрите выступление Сон Гэ.

Конечно, все знают, что вход без сохранения состояния имеет и недостатки.

Итак, сегодня Сонг Гэ хочет поговорить с вами о Spring Boot+OAuth2 для единого входа и использовать аннотацию @EnableOAuth2Sso для быстрой реализации функции единого входа.

Сонг Гэ по-прежнему рекомендует при чтении этой статьи сначала прочитать предыдущие статьи из этой серии, которые помогут вам лучше понять эту статью.

1. Создание проекта

В предыдущем случае Сонг Гэ всегда создавал сервер авторизации и сервер ресурсов отдельно, а в сегодняшнем случае, чтобы избежать проблем, я создал сервер авторизации и сервер ресурсов вместе (но я считаю, что после прочтения предыдущей статьи, вы должны быть в состоянии сделать это самостоятельно. разделить два сервера).

Итак, сегодня нам нужны всего три сервиса:

проект порт описывать
auth-server 1111 Сервер авторизации + сервер ресурсов
client1 1112 Подсистема 1
client2 1113 Подсистема 2

auth-server используется для игры в роли сервера авторизации + сервера ресурсов, а client1 и client2 играют роль подсистемы соответственно.В будущем, после того, как client1 успешно войдет в систему, мы также сможем получить доступ к client2, так что мы можем увидеть эффект единого входа.

Мы можем создать проект Maven с именем oauth2-sso в качестве родительского проекта.

2. Единый удостоверяющий центр

Далее построим единый удостоверяющий центр.

Сначала мы создаем модуль с именем auth-server и при создании добавляем следующие зависимости:

После того, как проект будет успешно создан, этот модуль будет играть роль сервера авторизации + сервера ресурсов, поэтому мы сначала добавляем в класс запуска этого проекта аннотацию @EnableResourceServer, указывающую, что это сервер ресурсов:

@SpringBootApplication
@EnableResourceServer
public class AuthServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(AuthServerApplication.class, args);
    }

}

Далее настраиваем сервер авторизации.Поскольку сервер ресурсов и сервер авторизации объединены воедино, настройка сервера авторизации избавит от многих хлопот:

@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("javaboy")
                .secret(passwordEncoder.encode("123"))
                .autoApprove(true)
                .redirectUris("http://localhost:1112/login", "http://localhost:1113/login")
                .scopes("user")
                .accessTokenValiditySeconds(7200)
                .authorizedGrantTypes("authorization_code");

    }
}

Здесь нам нужно только просто настроить информацию клиента.Настройка здесь очень проста.В предыдущих статьях тоже об этом говорилось.Если вы не поняли,то можете обратиться к предыдущим статьям из этой серии:Этот случай написан, и вы боитесь, что не сможете понять процесс входа в систему OAuth2 с интервьюером?.

Конечно, для простоты информационная конфигурация клиента основана на памяти.Если вы хотите хранить информацию о клиенте в базе данных, это также возможно.Можно ли хранить токены OAuth2 в Redis? Играйте больше и больше!

Далее настроим Spring Security:

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/login.html", "/css/**", "/js/**", "/images/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
                .antMatchers("/login")
                .antMatchers("/oauth/authorize")
                .and()
                .authorizeRequests().anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("sang")
                .password(passwordEncoder().encode("123"))
                .roles("admin");
    }
}

Что касается конфигурации Spring Security, если вы этого не понимаете, вы можете взглянуть на серию Spring Security, которую Song Ge недавно сериализует.

Я дам грубый обзор здесь:

  1. Сначала предоставьте экземпляр BCryptPasswordEncoder для шифрования и расшифровки пароля.
  2. Поскольку я настроил страницу входа, эти статические ресурсы в WebSecurity возложены на квадрат.
  3. В HttpSecurity мы выпускаем конечные точки, связанные с аутентификацией, и одновременно настраиваем страницу входа и интерфейс входа.
  4. В AuthenticationManagerBuilder предусмотрен пользователь на основе памяти (его можно настроить на загрузку из базы данных согласно седьмой статье серии Spring Security).
  5. Есть еще один ключевой момент, поскольку сервер ресурсов и сервер авторизации находятся вместе, поэтому нам нужна аннотация @Order для повышения приоритета конфигурации Spring Security.

И SecurityConfig, и AuthServerConfig — это вещи, которые должен предоставить сервер авторизации (если вы хотите разделить сервер авторизации и сервер ресурсов, обратите внимание на это предложение), затем нам также необходимо предоставить интерфейс, который предоставляет информацию о пользователе (если сервер авторизации и сервер ресурсов разделены, обратите внимание на это предложение) Сервер и сервер ресурсов разделены, этот интерфейс будет предоставлен сервером ресурсов):

@RestController
public class UserController {
    @GetMapping("/user")
    public Principal getCurrentUser(Principal principal) {
        return principal;
    }
}

Наконец, мы настраиваем порт проекта в application.properties:

server.port=1111

Кроме того, сам Сон Гэ заранее подготовил страницу входа в систему следующим образом:

Скопируйте html, css, js и т. д., относящиеся к странице входа, в каталог resources/static:

Эта страница очень проста, просто форма входа, я перечисляю основные части:

<form action="/login" method="post">
    <div class="input">
        <label for="name">用户名</label>
        <input type="text" name="username" id="name">
        <span class="spin"></span>
    </div>
    <div class="input">
        <label for="pass">密码</label>
        <input type="password" name="password" id="pass">
        <span class="spin"></span>
    </div>
    <div class="button login">
        <button type="submit">
            <span>登录</span>
            <i class="fa fa-check"></i>
        </button>
    </div>
</form>

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

Исходный код можно скачать в конце статьи.

После этого наша унифицированная платформа входа в систему аутентификации в порядке.

3. Создание клиента

Затем давайте создадим клиентский проект, создадим проект Spring Boot с именем client1 и добавим следующие зависимости:

После успешного создания проекта давайте настроим Spring Security:

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
    }
}

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

Далее мы предоставляем тестовый интерфейс в client1:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication.getName() + Arrays.toString(authentication.getAuthorities().toArray());
    }
}

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

Далее нам нужно настроить информацию, связанную с oauth2, в application.properties client1:

security.oauth2.client.client-secret=123
security.oauth2.client.client-id=javaboy
security.oauth2.client.user-authorization-uri=http://localhost:1111/oauth/authorize
security.oauth2.client.access-token-uri=http://localhost:1111/oauth/token
security.oauth2.resource.user-info-uri=http://localhost:1111/user

server.port=1112

server.servlet.session.cookie.name=s1

Конфигурация тут тоже знакомая, давайте посмотрим:

  1. client-secret — это секрет клиента.
  2. client-id — это идентификатор клиента.
  3. user-authorization-uri — это конечная точка авторизации пользователя.
  4. access-token-uri — это конечная точка для получения токена.
  5. user-info-uri — это интерфейс для получения информации о пользователе (полученной с сервера ресурсов).
  6. Наконец, настройте порт и дайте файлу cookie имя.

После этого наш client1 даже настроен.

Таким же образом снова настроим клиент 2. Клиент 2 и клиент 1 точно такие же, то есть имена куки разные (их можно выбрать по желанию, они могут быть разными).

4. Тест

Далее запускаем auth-server, client1 и client2 соответственно.Сначала пытаемся зайти на hello интерфейс в client1.В это время он автоматически перейдет к единому центру аутентификации:

Затем введите имя пользователя и пароль для входа.

После успешного входа он автоматически вернется к приветственному интерфейсу client1 следующим образом:

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

Хорошо, после этого наш единый вход прошел успешно.

5. Анализ процесса

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

  1. Сначала мы переходим на интерфейс /hello клиента1, но доступ к этому интерфейсу возможен только при входе в систему, поэтому наш запрос перехватывается, после перехвата система перенаправляет нас на интерфейс /login клиента1, что позволяет нам войти Войти.

  1. Когда мы обращаемся к интерфейсу входа в систему client1, поскольку мы настроили аннотацию @EnableOAuth2Sso, эта операция будет снова перехвачена, и перехватчик единого входа автоматически инициирует запрос на получение кода авторизации в соответствии с нашей конфигурацией в application.properties:

  1. Запрос, отправленный на втором этапе, — это запрос чего-либо на службе auth-server.Конечно, этот запрос не может избежать необходимости сначала войти в систему, поэтому он перенаправляется на страницу входа в систему auth-server, которая является унифицированной центр аутентификации, который видят все.
  2. В едином серьезном центре мы выполнили функцию входа в систему.После завершения входа в систему мы продолжим выполнение запроса на втором этапе.На данный момент мы можем успешно получить код авторизации.

  1. После получения кода авторизации он будет перенаправлен на страницу входа нашего клиента1 в это время, но на самом деле у нашего клиента1 нет страницы входа, поэтому эта операция все равно будет перехвачена.В это время перехваченный адрес содержит код авторизации.С кодом авторизации сделайте запрос к auth-серверу в классе OAuth2ClientAuthenticationProcessingFilter, и вы сможете получить access_token (см.:Этот случай написан, и вы боитесь, что не сможете понять процесс входа в систему OAuth2 с интервьюером?).
  2. После получения access_token на пятом шаге отправьте запрос на адрес user-info-uri, который мы настроили для получения информации о пользователе для входа, и после получения информации о пользователе снова выполните процесс входа в Spring Security на client1, который OK.

Хорошо, в этой статье говорилось о некоторых проблемах с единым входом SpringBoot + OAuth2 с моими друзьями Полный адрес загрузки случая:GitHub.com/Len VE/OAuth…

Если ваши друзья сочтут это полезным, не забудьте нажать и поддержать Songge.