1. 개요
이 짧은 사용방법(예제)에서는 기존 DataSource 구성 을 사용하여 JDBC 인증을 수행하기 위해 Spring에서 제공하는 기능을 탐색합니다 .
Database-backed UserDetailsService를 사용한 인증 게시물 에서 UserDetailService 인터페이스를 직접 구현하여 이를 달성하기 위한 한 가지 접근 방식을 분석했습니다.
이번에는 AuthenticationManagerBuilder#jdbcAuthentication 지시문을 사용하여 이 간단한 접근 방식의 장단점을 분석해 보겠습니다.
2. 임베디드 H2 연결 사용
먼저 내장된 H2 데이터베이스를 사용하여 인증을 달성하는 방법을 분석합니다.
대부분의 Spring Boot 자동 구성이 이 시나리오를 위해 기본적으로 준비되어 있기 때문에 이는 쉽게 달성할 수 있습니다.
2.1. 의존성 및 데이터베이스 구성
이전 Spring Boot With H2 Database 게시물 의 지침에 따라 시작하겠습니다 .
- 해당 spring-boot-starter-data-jpa 및 h2 의존성 을 포함합니다.
- 애플리케이션 속성으로 데이터베이스 연결 구성
- H2 콘솔 활성화
2.2. JDBC 인증 구성
Spring Security의 AuthenticationManagerBuilder 구성 도우미 를 사용 하여 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 지시문 은 기본 스키마를 채우는 데이터베이스 스크립트를 추가하여 사용자와 권한을 저장할 수 있도록 합니다.
이 기본 사용자 스키마는 Spring Security 부록 에 문서화되어 있습니다 .
마지막으로 프로그래밍 방식으로 기본 사용자로 데이터베이스에 항목을 생성합니다.
2.3. 구성 확인
인증된 Principal 정보 를 검색하는 매우 간단한 엔드포인트를 생성해 보겠습니다 .
@RestController
@RequestMapping("/principal")
public class UserController {
@GetMapping
public Principal retrievePrincipal(Principal principal) {
return principal;
}
}
또한 H2 콘솔에 대한 액세스를 허용하면서 이 엔드포인트를 보호합니다.
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity)
throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/h2-console/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin();
httpSecurity.csrf()
.ignoringAntMatchers("/h2-console/**");
httpSecurity.headers()
.frameOptions()
.sameOrigin();
return http.build();
}
}
참고: 여기 에서는 Spring Boot에 의해 구현된 이전 Security 구성을 재현하고 있지만 실제 시나리오에서는 H2 콘솔을 전혀 활성화하지 않을 것입니다.
이제 애플리케이션을 실행하고 H2 콘솔을 탐색합니다. Spring이 임베디드 데이터베이스에 사용자 및 권한 이라는 두 개의 테이블을 생성 하고 있음을 확인할 수 있습니다 .
이들의 구조는 앞에서 언급한 Spring Security 부록에 정의된 구조에 해당합니다.
마지막으로 /principal 엔드포인트를 인증하고 요청 하여 사용자 세부 정보를 포함한 관련 정보를 확인합니다.
2.4. 후드
이 게시물의 시작 부분에서 UserDetailsService 인터페이스를 구현하는 데이터베이스 지원 인증을 사용자 지정 하는 방법을 설명하는 사용방법(예제) 링크를 제공했습니다 . 후드 아래에서 작동하는 방식을 이해하려면 해당 게시물을 살펴보는 것이 좋습니다.
이 경우 Spring Security에서 제공하는 동일한 인터페이스의 구현에 의존하고 있습니다. JdbcDaoImpl . _
이 클래스를 탐색하면 사용하는 UserDetails 구현과 데이터베이스에서 사용자 정보를 검색하는 메커니즘을 볼 수 있습니다.
이 간단한 시나리오에서는 꽤 잘 작동하지만 데이터베이스 스키마를 사용자 지정하려는 경우나 다른 데이터베이스 공급업체를 사용하려는 경우에도 몇 가지 단점이 있습니다.
다른 JDBC 서비스를 사용하도록 구성을 변경하면 어떻게 되는지 살펴보겠습니다.
3. 다른 데이터베이스에 대한 스키마 조정
이 섹션에서는 MySQL 데이터베이스를 사용하여 프로젝트에서 인증을 구성합니다.
다음에 살펴보겠지만 이를 달성하려면 기본 스키마를 사용하지 않고 자체 스키마를 제공해야 합니다.
3.1. 의존성 및 데이터베이스 구성
우선 h2 의존성을 제거하고 해당 MySQL 라이브러리로 교체해 보겠습니다.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
언제나처럼 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 지시문 과 함께 제공되는 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.sql.init.mode=always
spring.jpa.hibernate.ddl-auto=none
결과적으로 이제 애플리케이션을 올바르게 시작 하여 엔드포인트에서 Principal 데이터를 인증 및 검색할 수 있습니다.
또한 spring.sql.init.mode 속성은 Spring Boot 2.5.0에서 도입되었습니다. 이전 버전의 경우 spring.datasource.initialization-mode 를 사용해야 합니다.
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 user@email.pass/pass
INSERT INTO bael_users (name, email, password, enabled)
values ('user',
'user@email.com',
'$2a$10$8.UnVuG9HHgffUDAlk8qfOuVGkqRzgVymGe07xd00DMxs.AQubh4a',
1);
INSERT INTO authorities (email, authority)
values ('user@email.com', 'ROLE_USER');
4.2. 새 스키마로 애플리케이션 실행
애플리케이션을 실행해 봅시다. 올바르게 초기화되며 스키마가 정확하므로 의미가 있습니다.
이제 로그인을 시도하면 자격 증명을 제시할 때 오류 메시지가 표시됩니다.
Spring Security는 여전히 데이터베이스에서 사용자 이름 필드를 찾고 있습니다. 다행스럽게도 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 구현을 만드는 것보다 훨씬 간단합니다. UserDetail 인터페이스를 구현하는 엔터티 및 클래스를 만들고 프로젝트에 리포지토리를 추가합니다.
물론 단점은 데이터베이스나 논리 가 Spring Security 솔루션에서 제공 하는 기본 전략과 다를 때 제공되는 유연성이 거의 없다는 것 입니다.
마지막으로 GitHub 리포지토리 에서 전체 예제를 살펴볼 수 있습니다 . 간단하게 하기 위해 이 예제에서 보여주지 않은 PostgreSQL을 사용하는 예제도 포함했습니다.