2019-12-06 10:39:55 -06:00
[[servlet-authentication-jdbc]]
= JDBC Authentication
2021-12-13 16:57:36 -06:00
Spring Security's `JdbcDaoImpl` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] to provide support for username-and-password-based authentication that is retrieved by using JDBC.
2019-12-06 10:39:55 -06:00
`JdbcUserDetailsManager` extends `JdbcDaoImpl` to provide management of `UserDetails` through the `UserDetailsManager` interface.
2021-12-13 16:57:36 -06:00
`UserDetails`-based authentication is used by Spring Security when it is configured to xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication.
2019-12-06 10:39:55 -06:00
2021-04-21 16:01:26 -05:00
In the following sections, we discuss:
2019-12-06 10:39:55 -06:00
* The <<servlet-authentication-jdbc-schema>> used by Spring Security JDBC Authentication
* <<servlet-authentication-jdbc-datasource>>
* <<servlet-authentication-jdbc-bean>>
[[servlet-authentication-jdbc-schema]]
== Default Schema
2021-04-21 16:01:26 -05:00
Spring Security provides default queries for JDBC-based authentication.
2019-12-06 10:39:55 -06:00
This section provides the corresponding default schemas used with the default queries.
2021-04-21 16:01:26 -05:00
You need to adjust the schema to match any customizations to the queries and the database dialect you use.
2019-12-06 10:39:55 -06:00
[[servlet-authentication-jdbc-schema-user]]
=== User Schema
`JdbcDaoImpl` requires tables to load the password, account status (enabled or disabled) and a list of authorities (roles) for the user.
[NOTE]
====
The default schema is also exposed as a classpath resource named `org/springframework/security/core/userdetails/jdbc/users.ddl`.
====
.Default User Schema
====
[source,sql]
----
create table users(
username varchar_ignorecase(50) not null primary key,
2020-05-18 22:35:57 +02:00
password varchar_ignorecase(500) not null,
2019-12-06 10:39:55 -06:00
enabled boolean not null
);
create table authorities (
username varchar_ignorecase(50) not null,
authority varchar_ignorecase(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
----
====
2021-04-21 16:01:26 -05:00
Oracle is a popular database choice but requires a slightly different schema:
2019-12-06 10:39:55 -06:00
.Default User Schema for Oracle Databases
====
[source,sql]
----
CREATE TABLE USERS (
USERNAME NVARCHAR2(128) PRIMARY KEY,
PASSWORD NVARCHAR2(128) NOT NULL,
ENABLED CHAR(1) CHECK (ENABLED IN ('Y','N') ) NOT NULL
);
CREATE TABLE AUTHORITIES (
USERNAME NVARCHAR2(128) NOT NULL,
AUTHORITY NVARCHAR2(128) NOT NULL
);
ALTER TABLE AUTHORITIES ADD CONSTRAINT AUTHORITIES_UNIQUE UNIQUE (USERNAME, AUTHORITY);
ALTER TABLE AUTHORITIES ADD CONSTRAINT AUTHORITIES_FK1 FOREIGN KEY (USERNAME) REFERENCES USERS (USERNAME) ENABLE;
----
====
[[servlet-authentication-jdbc-schema-group]]
=== Group Schema
2021-04-21 16:01:26 -05:00
If your application uses groups, you need to provide the groups schema:
2019-12-06 10:39:55 -06:00
.Default Group Schema
====
[source,sql]
----
create table groups (
id bigint generated by default as identity(start with 0) primary key,
group_name varchar_ignorecase(50) not null
);
create table group_authorities (
group_id bigint not null,
authority varchar(50) not null,
constraint fk_group_authorities_group foreign key(group_id) references groups(id)
);
create table group_members (
id bigint generated by default as identity(start with 0) primary key,
username varchar(50) not null,
group_id bigint not null,
constraint fk_group_members_group foreign key(group_id) references groups(id)
);
----
====
[[servlet-authentication-jdbc-datasource]]
== Setting up a DataSource
Before we configure `JdbcUserDetailsManager`, we must create a `DataSource`.
2021-04-21 16:01:26 -05:00
In our example, we set up an https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#jdbc-embedded-database-support[embedded DataSource] that is initialized with the <<servlet-authentication-jdbc-schema,default user schema>>.
2019-12-06 10:39:55 -06:00
2020-01-09 20:12:19 -06:00
.Embedded Data Source
2019-12-06 10:39:55 -06:00
====
2020-01-09 20:12:19 -06:00
.Java
[source,java,role="primary"]
2019-12-06 10:39:55 -06:00
----
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(H2)
2022-02-15 11:24:23 +01:00
.addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
2019-12-06 10:39:55 -06:00
.build();
}
----
2020-01-09 20:12:19 -06:00
.XML
[source,xml,role="secondary"]
2019-12-06 10:39:55 -06:00
----
<jdbc:embedded-database>
<jdbc:script location="classpath:org/springframework/security/core/userdetails/jdbc/users.ddl"/>
</jdbc:embedded-database>
----
2020-01-14 17:20:18 +01:00
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.setType(H2)
2022-02-15 11:24:23 +01:00
.addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
2020-01-14 17:20:18 +01:00
.build()
}
----
2019-12-06 10:39:55 -06:00
====
2021-04-21 16:01:26 -05:00
In a production environment, you want to ensure that you set up a connection to an external database.
2019-12-06 10:39:55 -06:00
[[servlet-authentication-jdbc-bean]]
== JdbcUserDetailsManager Bean
2021-12-13 16:57:36 -06:00
In this sample, we use xref:features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode a password value of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`.
2021-08-10 15:21:42 -05:00
See the xref:features/authentication/password-storage.adoc#authentication-password-storage[PasswordEncoder] section for more details about how to store passwords.
2019-12-06 10:39:55 -06:00
2020-01-09 20:12:19 -06:00
.JdbcUserDetailsManager
2019-12-06 10:39:55 -06:00
====
2020-01-09 20:12:19 -06:00
.Java
2020-02-10 10:47:21 -06:00
[source,java,role="primary",attrs="-attributes"]
2019-12-06 10:39:55 -06:00
----
@Bean
UserDetailsManager users(DataSource dataSource) {
UserDetails user = User.builder()
.username("user")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER", "ADMIN")
.build();
JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
2020-05-18 22:35:57 +02:00
users.createUser(user);
users.createUser(admin);
2021-08-25 13:31:00 -05:00
return users;
2019-12-06 10:39:55 -06:00
}
----
2020-01-09 20:12:19 -06:00
.XML
2020-02-10 10:47:21 -06:00
[source,xml,role="secondary",attrs="-attributes"]
2019-12-06 10:39:55 -06:00
----
<jdbc-user-service>
<user name="user"
password="{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW"
authorities="ROLE_USER" />
<user name="admin"
password="{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW"
authorities="ROLE_USER,ROLE_ADMIN" />
</jdbc-user-service>
----
2020-01-14 17:20:18 +01:00
.Kotlin
2020-02-10 10:47:21 -06:00
[source,kotlin,role="secondary",attrs="-attributes"]
2020-01-14 17:20:18 +01:00
----
@Bean
fun users(dataSource: DataSource): UserDetailsManager {
val user = User.builder()
.username("user")
.password("{bcrypt}$2a$10\$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER")
.build();
val admin = User.builder()
.username("admin")
.password("{bcrypt}$2a$10\$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER", "ADMIN")
.build();
val users = JdbcUserDetailsManager(dataSource)
users.createUser(user)
users.createUser(admin)
return users
}
----
2019-12-06 10:39:55 -06:00
====