Quarkus comes with a lot of authentication mechanisms. Today we are going to implement a simple one with Form-Based Authentication. Quarkus has good documentation but the example for authentication is all over the place. We will create one here just for future reference.
You can visit the documentation here.
A quick description of what we want to do is:
- we have some REST endpoints
- we want to secure the endpoints
- with minimal effort
- username
and password saved in the database
These are the basic requirement.
To begin, check out my previous source code from https://github.com/devilkazuya99/inventory-service
It should have 5 existing endpoints in InventoryResource.java.
The first thing to do is to enable form-based authentication in the application.properties file.
Now we need to write some entity class to hold our user's credentials.
@Entity
@UserDefinition
public class AppUser extends PanacheEntity {
@Username
public String username;
@Password
public String password;
@ManyToMany(cascade = CascadeType.ALL)
@Roles
public List<UserRole> roles = new ArrayList<>();;
public static void add(String username, String password, String role) {
AppUser user = new AppUser();
user.username = username;
user.password = BcryptUtil.bcryptHash(password);
UserRole userRole = new UserRole();
userRole.role = role;
user.roles.add(userRole);
user.persist();
}
}
@Entity
public class UserRole extends PanacheEntity {
@ManyToMany(mappedBy = "roles")
public List<AppUser> users;
@RolesValue
public String role;
}
Our AppUser has a username and password. It also can have a list of UserRoles.
In listCategory() endpoint in InventoryResource.java, add a @RolesAllowed("user") annotation to the function.
Lastly, add 2 static files, one for login and one for the error page.
The error page is just a dummy page with static content. We will explain more on the login page.
<div class="container">
<section id="content">
<form action="/j_security_check" method="POST">
<h1>Login Form</h1>
<div>
<input type="text" placeholder="Username" required="" id="username" name="j_username" />
</div>
<div>
<input type="password" placeholder="Password" required="" id="password" name="j_password" />
</div>
<div>
<input type="submit" value="Log in" />
<a href="#">Lost your password?</a>
<a href="#">Register</a>
</div>
</form><!-- form -->
</section><!-- content -->
</div><!-- container -->
In short, the login form will POST to '/j_security_check', which is provided by Quarkus dark art. The default username is 'j_username' and the password is 'j_password'. That's all it is.
These are default values that can be modified. For more details see here.
Now, starts the application with this command (that you need to remember for the rest of your life).
mvn compile quarkus:dev
Using a web browser we first browse the product page: http://localhost:8080/inventory/product
We will get an empty array as the response.
[]
Now we browse the catalog page: http://localhost:8080/inventory/category
This time we got redirected to the login page.
The Quarkus dark art is working so far. We annotated the endpoint previously to only allows user with role "user" to access. It is looking good.We forgot one important thing. We don't have any user to login. Let's go back and create some.
We create a Startup class. (you can give it any name)
@Singleton
public class Startup {
@Transactional
public void loadUsers(@Observes StartupEvent evt) {
// reset and load all test users
AppUser.deleteAll();
AppUser.add("admin", "admin", "admin");
AppUser.add("user", "user", "user");
}
}
Pay attention to the loadUsers function. The function has a @Observes annotation, which will listen to the Quarkus StartEvent. So every time Quarkus starts up, this function will be triggered. When it happened, we cleared the table, and add 2 dummy users to it.
Now, we go back to the category page, http://localhost:8080/inventory/category, key in the username and password, VoilĂ , we can see the empty JSON array.
[]
This is it, folks. Quarkus Form-Based Authentication example. For the next experiment, we are going to put all the services together, and then try out other authentication methods that will fit our design. So stay tuned.
The source code for this post can be found: https://github.com/devilkazuya99/inventory-service/tree/form-authentication
(make sure you select the correct branch)
No comments:
Post a Comment