You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.7 KiB
211 lines
6.7 KiB
[[servlet-authentication-jdbc]] |
|
= JDBC Authentication |
|
|
|
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. |
|
`JdbcUserDetailsManager` extends `JdbcDaoImpl` to provide management of `UserDetails` through the `UserDetailsManager` interface. |
|
`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. |
|
|
|
In the following sections, we discuss: |
|
|
|
* 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 |
|
|
|
Spring Security provides default queries for JDBC-based authentication. |
|
This section provides the corresponding default schemas used with the default queries. |
|
You need to adjust the schema to match any customizations to the queries and the database dialect you use. |
|
|
|
[[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, |
|
password varchar_ignorecase(500) not null, |
|
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); |
|
---- |
|
|
|
Oracle is a popular database choice but requires a slightly different schema: |
|
|
|
.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 |
|
|
|
If your application uses groups, you need to provide the groups schema: |
|
|
|
.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`. |
|
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>>. |
|
|
|
.Embedded Data Source |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
@Bean |
|
DataSource dataSource() { |
|
return new EmbeddedDatabaseBuilder() |
|
.setType(H2) |
|
.addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION) |
|
.build(); |
|
} |
|
---- |
|
|
|
XML:: |
|
+ |
|
[source,xml,role="secondary"] |
|
---- |
|
<jdbc:embedded-database> |
|
<jdbc:script location="classpath:org/springframework/security/core/userdetails/jdbc/users.ddl"/> |
|
</jdbc:embedded-database> |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
@Bean |
|
fun dataSource(): DataSource { |
|
return EmbeddedDatabaseBuilder() |
|
.setType(H2) |
|
.addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION) |
|
.build() |
|
} |
|
---- |
|
====== |
|
|
|
In a production environment, you want to ensure that you set up a connection to an external database. |
|
|
|
[[servlet-authentication-jdbc-bean]] |
|
== JdbcUserDetailsManager Bean |
|
|
|
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+`. |
|
See the xref:features/authentication/password-storage.adoc#authentication-password-storage[PasswordEncoder] section for more details about how to store passwords. |
|
|
|
.JdbcUserDetailsManager |
|
|
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary",attrs="-attributes"] |
|
---- |
|
@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); |
|
users.createUser(user); |
|
users.createUser(admin); |
|
return users; |
|
} |
|
---- |
|
|
|
XML:: |
|
+ |
|
[source,xml,role="secondary",attrs="-attributes"] |
|
---- |
|
<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> |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary",attrs="-attributes"] |
|
---- |
|
@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 |
|
} |
|
---- |
|
======
|
|
|