Spring Security:JDBC認証の調査

1. 概要

この短いチュートリアルでは、既存のlink:/spring-boot-configure-data-source-programmatic[_DataSource_]構成を使用してJDBC認証を実行するために、Springが提供する機能を調べます。
link:/spring-security-authentication-with-a-database [データベースがサポートするUserDetailsS​​erviceによる認証]投稿では、__ UserDetailService __interfaceを独自に実装することにより、これを達成するための1つのアプローチを分析しました。
今回は、_AuthenticationManagerBuilder#jdbcAuthentication_ディレクティブを使用して、この単純なアプローチの長所と短所を分析します。

2. 埋め込みH2接続の使用

まず、埋め込みH2データベースを使用して認証を実現する方法を分析します。
このシナリオでは、Spring Bootの自動構成のほとんどがすぐに使用できるため、これを簡単に実現できます。

2.1. 依存関係とデータベース構成

以前のlink:/spring-boot-h2-database[H2データベースを使用したスプリングブート]投稿の指示に従うことから始めましょう:
  1. 対応するspring-boot-starter-data-jpa andand__h2_を含めます
    依存関係

  2. アプリケーションのプロパティを使用してデータベース接続を構成する

  3. H2コンソールを有効にする

2.2. JDBC認証の構成

@Autowired
private DataSource dataSource;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
  throws Exception {
    auth.jdbcAuthentication()
      .dataSource(dataSource)
      .withDefaultSchema()
      .withUser(User.withUsername("user")
        .password(passwordEncoder().encode("pass"))
        .roles("USER"));
}

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
ご覧のとおり、自動構成された_DataSource._を使用しています。_withDefaultSchema_ディレクティブは、デフォルトのスキーマを設定するデータベーススクリプトを追加し、ユーザーと権限を保存できるようにします。
この基本的なユーザースキーマは、https://docs.spring.io/spring-security/site/docs/current/reference/html/appendix.html#user-schema [Spring Security Appendix]に記載されています。
最後に、デフォルトのユーザーを使用してプログラムでデータベースにエントリを作成しています。

2.3. 構成の検証

認証済みの_Principal_情報を取得するための非常に単純なエンドポイントを作成しましょう。
@RestController
@RequestMapping("/principal")
public class UserController {

    @GetMapping
    public Principal retrievePrincipal(Principal principal) {
        return principal;
    }
}
さらに、H2コンソールへのアクセスを許可しながら、このエンドポイントを保護します。
@Configuration
public class SecurityConfiguration
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity)
      throws Exception {
        httpSecurity.authorizeRequests()
          .antMatchers("/h2-console/**")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .formLogin();

        httpSecurity.csrf()
          .ignoringAntMatchers("/h2-console/**");
        httpSecurity.headers()
          .frameOptions()
          .sameOrigin();
    }
}
注:ここでは、Spring Bootによって実装されたhttps://github.com/spring-projects/spring-boot/issues/10435 [以前のセキュリティ構成]を再現していますが、*実際のシナリオでは、おそらくt H2コンソールをすべて有効にします。*
次に、アプリケーションを実行し、H2コンソールを参照します。 * Springが埋め込みデータベースに_users_と_authorities ._ *の2つのテーブルを作成していることを確認できます。
それらの構造は、前述のSpring Security付録で定義されている構造に対応しています。
最後に、_ / principal_エンドポイントを認証して要求し、ユーザーの詳細を含む関連情報を確認します。

2.4. フードの下

この投稿の冒頭で、__ UserDetailsS​​ervice __interface *を実装するデータベースバックアップ認証をカスタマイズする方法を説明したチュートリアルへのリンクを示しました。内部で物事がどのように機能するかを理解したい場合は、その投稿をご覧になることを強くお勧めします。
*この場合、Spring Securityが提供するこの同じインターフェースの実装に依存しています。 the _JdbcDaoImpl _。*
このクラスを調べると、使用する__UserDetails __implementationと、データベースからユーザー情報を取得するメカニズムが表示されます。
これは、この単純なシナリオではかなりうまく機能しますが、データベーススキーマをカスタマイズする場合、または別のデータベースベンダーを使用する場合でも、いくつかの欠点があります。
別のJDBCサービスを使用するように構成を変更するとどうなるか見てみましょう。

3. 別のデータベースへのスキーマの適応

このセクションでは、MySQLデータベースを使用してプロジェクトの認証を構成します。
次に見るように、これを達成するために、デフォルトのスキーマの使用を避け、独自のスキーマを提供する必要があります。

3.1. 依存関係とデータベース構成

まず、__h2 ___dependencyを削除して、対応するMySQLライブラリに置き換えましょう。
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.17</version>
</dependency>
いつものように、https://search.maven.org/search?q = a:mysql-connector-java%20g:mysql [Maven Central]でライブラリの最新バージョンを検索できます。
それに応じて、アプリケーションのプロパティを再設定してみましょう。
spring.datasource.url=
  jdbc:mysql://localhost:3306/jdbc_authentication
spring.datasource.username=root
spring.datasource.password=pass

3.2. デフォルト設定の実行

もちろん、これらは実行中のMySQLサーバーに接続するようにカスタマイズする必要があります。 テストのために、ここではDockerを使用して新しいインスタンスを開始します。
docker run -p 3306:3306
  --name bael-mysql
  -e MYSQL_ROOT_PASSWORD=pass
  -e MYSQL_DATABASE=jdbc_authentication
  mysql:latest
ここでプロジェクトを実行して、デフォルトの構成がMySQLデータベースに適しているかどうかを確認しましょう。
実際、_SQLSyntaxErrorException_が原因で、アプリケーションは開始できません。 これは実際に理にかなっています。前述したように、デフォルトの自動構成のほとんどはHSQLDBに適しています。
この場合、* __ withDefaultSchema __directiveで提供されるDDLスクリプトは、MySQLに適さない方言を使用します。*
したがって、このスキーマの使用を避け、独自のスキーマを提供する必要があります。

3.3. 認証設定の適応

デフォルトのスキーマを使用したくないので、_AuthenticationManagerBuilder_構成から適切なステートメントを削除する必要があります。
また、独自のSQLスクリプトを提供するため、ユーザーをプログラムで作成しようとすることを回避できます。
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
  throws Exception {
    auth.jdbcAuthentication()
      .dataSource(dataSource);
}
それでは、データベース初期化スクリプトを見てみましょう。
まず、_schema.sql_:
CREATE TABLE users (
  username VARCHAR(50) NOT NULL,
  password VARCHAR(100) NOT NULL,
  enabled TINYINT NOT NULL DEFAULT 1,
  PRIMARY KEY (username)
);

CREATE TABLE authorities (
  username VARCHAR(50) NOT NULL,
  authority VARCHAR(50) NOT NULL,
  FOREIGN KEY (username) REFERENCES users(username)
);

CREATE UNIQUE INDEX ix_auth_username
  on authorities (username,authority);
そして、data.sql:
-- User user/pass
INSERT INTO users (username, password, enabled)
  values ('user',
    '$2a$10$8.UnVuG9HHgffUDAlk8qfOuVGkqRzgVymGe07xd00DMxs.AQubh4a',
    1);

INSERT INTO authorities (username, authority)
  values ('user', 'ROLE_USER');
最後に、いくつかの他のアプリケーションプロパティを変更する必要があります。
  • Hibernateがスキーマを作成することを期待していないため、
    ddl-autoプロパティを無効にする必要があります

  • デフォルトでは、Spring Bootは組み込みのデータソースのみを初期化します
    データベース、ここではそうではありません:

spring.datasource.initialization-mode=always
spring.jpa.hibernate.ddl-auto=none
その結果、アプリケーションを正しく起動し、エンドポイントから__Principal __dataを認証および取得できるようになります。

4. 異なるスキーマへのクエリの適応

さらに一歩進めましょう。 デフォルトのスキーマが私たちのニーズにちょうど合わないと想像してください。

4.1. デフォルトスキーマの変更

たとえば、デフォルトのデータベースとは少し異なる構造のデータベースがすでにあるとします。
CREATE TABLE bael_users (
  name VARCHAR(50) NOT NULL,
  email VARCHAR(50) NOT NULL,
  password VARCHAR(100) NOT NULL,
  enabled TINYINT NOT NULL DEFAULT 1,
  PRIMARY KEY (email)
);

CREATE TABLE authorities (
  email VARCHAR(50) NOT NULL,
  authority VARCHAR(50) NOT NULL,
  FOREIGN KEY (email) REFERENCES bael_users(email)
);

CREATE UNIQUE INDEX ix_auth_email on authorities (email,authority);
最後に、_data.sql_スクリプトもこの変更に適応します。
-- User [email protected]/pass
INSERT INTO bael_users (name, email, password, enabled)
  values ('user',
    '[email protected]',
    '$2a$10$8.UnVuG9HHgffUDAlk8qfOuVGkqRzgVymGe07xd00DMxs.AQubh4a',
    1);

INSERT INTO authorities (email, authority)
  values ('[email protected]', 'ROLE_USER');

4.2. 新しいスキーマでアプリケーションを実行する

アプリケーションを起動しましょう。 これは正しく初期化されます。これは、スキーマが正しいため、理にかなっています。
ここで、ログインしようとすると、資格情報を提示するときにエラーが表示されます。
Spring Securityはまだデータベースで__username __fieldを探しています。 幸いなことに、JDBC認証構成では、認証プロセスでユーザーの詳細を取得するために使用されるクエリをカスタマイズすることができます。*

4.3. 検索クエリのカスタマイズ

クエリの適応は非常に簡単です。 _AuthenticationManagerBuilder_を構成するときは、独自のSQLステートメントを提供するだけです。
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
  throws Exception {
    auth.jdbcAuthentication()
      .dataSource(dataSource)
      .usersByUsernameQuery("select email,password,enabled "
        + "from bael_users "
        + "where email = ?")
      .authoritiesByUsernameQuery("select email,authority "
        + "from authorities "
        + "where email = ?");
}
アプリケーションをもう一度起動し、新しい資格情報を使用して_ / principal_エンドポイントにアクセスできます。

5. 結論

ご覧のとおり、このアプローチは独自の__UserDetailService __implementationを作成するよりもはるかに簡単です。これは困難なプロセスを意味します。 entities__UserDetail ___interfaceを実装するエンティティとクラスを作成し、リポジトリをプロジェクトに追加します。
*もちろん、データベースまたはロジックがSpring Securityソリューションによって提供されるデフォルトの戦略と異なる場合に提供される柔軟性がわずかです。
最後に、https://github.com/eugenp/tutorials/tree/master/spring-security-mvc-boot/src/main/java/org/baeldung/jdbcauthentication [our Github]で完全な例を見ることができます。倉庫]。 簡単にするために、このチュートリアルでは示していないPostgreSQLを使用した例を含めました。