Quarkus Security with Jakarta Persistence
Quarkus provides a Jakarta Persistence (formerly known as JPA) identity provider, similar to the JDBC identity provider, suitable for use with the Basic and Form-based Quarkus Security mechanisms, which require a combination of username and password credentials.
The Jakarta Persistence IdentityProvider
creates a SecurityIdentity
instance, which is used during user authentication to verify and authorize
access requests making your Quarkus application secure.
For an example of practical use of Basic authentication and Jakarta Persistence, see the Secure a Quarkus application with Basic authentication and Jakarta Persistence tutorial.
Jakarta Persistence entity specification
Quarkus security offers a Jakarta Persistence integration to collect usernames, passwords, and roles, and store them into Jakarta Persistence database entities.
The following Jakarta Persistence entity specification demonstrates how users' information needs to be stored in a Jakarta Persistence entity and properly mapped so that Quarkus can retrieve this information from a database.
-
The
@UserDefinition
annotation must be present on a Jakarta Persistence entity, regardless of whether simplified Hibernate ORM with Panache is used or not. -
The
@Username
and@Password
field types are alwaysString
. -
The
@Roles
field must either beString
,Collection<String>
, or aCollection<X>
, whereX
is an entity class with a singleString
field annotated as@RolesValue
. -
Each
String
role element type is parsed as a comma-separated list of roles.
The following example demonstrates storing security information by adding
annotations to the user
entity:
package org.acme.security.jpa;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import io.quarkus.elytron.security.common.BcryptUtil;
import io.quarkus.security.jpa.Password;
import io.quarkus.security.jpa.Roles;
import io.quarkus.security.jpa.UserDefinition;
import io.quarkus.security.jpa.Username;
@Entity
@Table(name = "test_user")
@UserDefinition (1)
public class User extends PanacheEntity {
@Username (2)
public String username;
@Password (3)
public String password;
@Roles (4)
public String role;
/**
* Adds a new user to the database
* @param username the username
* @param password the unencrypted password (it will be encrypted with bcrypt)
* @param role the comma-separated roles
*/
public static void add(String username, String password, String role) { (5)
User user = new User();
user.username = username;
user.password = BcryptUtil.bcryptHash(password);
user.role = role;
user.persist();
}
}
The security-jpa
extension initializes only if a single entity is
annotated with @UserDefinition
.
1 | The @UserDefinition annotation must be present on a single entity, either
a regular Hibernate ORM entity or a Hibernate ORM with a Panache entity. |
2 | Indicates the field used for the username. |
3 | Indicates the field used for the password. By default, security-jpa uses
bcrypt-hashed passwords, or you can configure plain text or custom passwords
instead. |
4 | This indicates the comma-separated list of roles added to the target principal representation attributes. |
5 | This method allows you to add users while hashing passwords with the proper
bcrypt hash. |
Jakarta Persistence entity as storage of roles
Use the following example to store roles inside another Jakarta Persistence entity:
@UserDefinition
@Table(name = "test_user")
@Entity
public class User extends PanacheEntity {
@Username
public String name;
@Password
public String pass;
@ManyToMany
@Roles
public List<Role> roles = new ArrayList<>();
}
@Entity
public class Role extends PanacheEntity {
@ManyToMany(mappedBy = "roles")
public List<ExternalRolesUserEntity> users;
@RolesValue
public String role;
}
Password storage and hashing
When developing applications with Quarkus, you can decide how to manage password storage and hashing. You can choose to keep the default password and hashing settings of Quarkus, or you can hash passwords manually.
With the default option, passwords are stored and hashed with bcrypt under the Modular Crypt Format (MCF). While using MCF, the hashing algorithm, iteration count, and salt are stored as a part of the hashed value. As such, we do not need dedicated columns to keep them.
In cryptography, a salt is a name for random data used as an additional input to a one-way function that hashes data, a password, or a passphrase. |
To represent passwords stored in the database which were hashed using
different hashing algorithms, create a class that implements
org.wildfly.security.password.PasswordProvider
as shown in the example
below.
The following snippet shows how to set a custom password provider that represents a password which was hashed with the SHA256 hashing algorithm.
import org.wildfly.security.password.Password;
import org.wildfly.security.password.PasswordProvider;
@UserDefinition
@Table(name = "test_user")
@Entity
public class CustomPasswordUserEntity {
@Id
@GeneratedValue
public Long id;
@Column(name = "username")
@Username
public String name;
@Column(name = "password")
@Password(value = PasswordType.CUSTOM, provider = CustomPasswordProvider.class)
public String pass;
@Roles
public String role;
}
public class CustomPasswordProvider implements PasswordProvider {
@Override
public Password getPassword(String passwordInDatabase) {
byte[] digest = DatatypeConverter.parseHexBinary(passwordInDatabase);
// Let the security runtime know that this passwordInDatabase is hashed using the SHA256 hashing algorithm
return SimpleDigestPassword.createRaw(SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_SHA_256, digest);
}
}
For quick creation of a hashed password, use |
For applications running in a production environment, do not store passwords as plain text. However, it is possible to store passwords as plain text with the
|
The Hibernate Multitenancy is
supported and you can store the user entity in a persistence unit with
enabled multitenancy. However, if your
|
Configuration property fixed at build time - All other configuration properties are overridable at runtime
Type |
Default |
|
---|---|---|
Selects the Hibernate ORM persistence unit. Default persistence unit is used when no value is specified. Environment variable: Show more |
string |
|