.. _user_auth_walkthrough: Creating a ``User`` Model ========================= The next few sections walk through the steps necessary to enable simple authentication in the ``coding-events`` app. Along the way, we will use some advanced concepts that you haven't fully learned. That's okay. We don't expect you to understand every detail the next few sections. However, you do need to understand the purpose of each step in enabling authentication. .. admonition:: Note While we'll use ``coding-events``, these steps would be the same for any other app. If you want to add simple authentication to a Spring Boot application in the future, reference this chapter. Before we can authenticate users, we need users to authenticate! We'll start by adding a ``User`` model. A ``User`` Model ---------------------- .. admonition:: Note You can use the `add-tags branch `_ as your starting point for this section. A model class representing users needs, at a minimum, fields representing username and password. In the ``models`` package, create a ``User`` class with ``@Entity`` and extending ``AbstractEntity``. It should have two string fields, ``username`` and ``pwHash``. We only need a getter for ``username``. .. sourcecode:: java :lineno-start: 13 @Entity public class User extends AbstractEntity { @NotNull private String username; @NotNull private String pwHash; public User() {} public User(String username, String password) { this.username = username; this.pwHash = password; } public String getUsername() { return username; } } Notice that the constructor takes a parameter named ``password`` and uses it to set the value of ``pwHash``. We mentioned :ref:`previously ` that we should never store passwords, so in a moment, we will update line 26 by creating a hash from the given password to store. .. admonition:: Note Our validation annotations on ``User`` are very lenient. This is okay, however, because we will validate user input used to make ``User`` objects using a DTO with more restrictive validation. Hashing Passwords ----------------- .. index:: ! bcrypt We'll use the bycrypt hash algorithm. One way to access this algorithm is via the following dependency: :: org.springframework.security:spring-security-crypto Add this as an ``implementation`` dependency in your ``build.gradle`` file. This dependency provides the ``BCryptPasswordEncoder`` class, which we will use to create and verify hashes. While our class needs one of these encoder objects, it does not need to be an instance variable. We'll make it static so it can be shared by all ``User`` objects. .. sourcecode:: java :lineno-start: 25 private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); In the constructor, we can use ``encoder`` to create a hash from the given password: .. sourcecode:: java :lineno-start: 29 public User(String username, String password) { this.username = username; this.pwHash = encoder.encode(password); } Our ``User`` objects should also be responsible for determining if a given password is a match for the hash stored by the object. We can do this using the ``encoder.matches()`` method. Let's put this behavior in a method at the bottom of our ``User`` class: .. sourcecode:: java :lineno-start: 38 public boolean isMatchingPassword(String password) { return encoder.matches(password, pwHash); } .. admonition:: Warning Notice that we are using ``encoder.matches()`` rather than directly comparing hash values. More explicitly, the following comparison will NOT work to compare hashes generated by bcrypt: .. sourcecode:: java public boolean isMatchingPassword(String password) { String candidateHash = encoder.encode(password); return candidateHash.equals(pwHash); } While our conceptual example in the previous section used direct comparison of hashes---and some hashing techniques allow you to do so---bcrypt does not. This is because bcrypt internally uses a technique called `salting `_, which requires additional steps before comparison. These additional steps are carried out by ``encoder.matches()``. Creating the ``UserRepository`` ------------------------------- As usual, we need a repository in order to access ``User`` objects stored in the database. This time, however, we add a twist. Create ``UserRepository`` in the ``data`` package, with the following contents: .. sourcecode:: java :lineno-start: 9 public interface UserRepository extends CrudRepository { User findByUsername(String username); } .. index:: ! query methods While our repository extends ``CrudRepository``, it also contains a new method, ``findByUsername``. Based on the method signature, it appears that this method is intended to take a username, and return the given user with that username. Indeed, when our application runs, the ``UserRepository`` will have such a method. Spring allows for additional, custom methods to be added to repository interfaces, as long as they follow some basic naming conventions. These conventions are straightforward to use, and allow you to create additional, more sophisticated query methods. Methods created in this way are called **query methods**, and their rules are defined in `Spring's documentation `_. .. admonition:: Note The final code for this section is available in the `user-model branch `_ of the ``coding-events-demo`` repository. Check Your Understanding ------------------------ .. admonition:: Question Why can we not use Java string comparison when evaluating values generated with bcrypt? #. bcrypt will never create two matching hashes. #. Java does not have a native string comparator method. #. Salting adds variance to hashes generated from the same plain text. #. Answers a and c. .. ans: c, Salting adds variance to hashes generated from the same plain text. .. admonition:: Question .. sourcecode:: java :lineno-start: 9 public interface UserRepository extends CrudRepository { User findByUsername(String username); } True/False: From the code block above, line 11 is missing a return statement. #. True #. False .. ans: False, line 11 is a special query method that takes advantage of logic written in Spring to determine how it functions.