Authentication and authorization

Sol
20 min readApr 30, 2022

--

Authentication

Why bother?

Data breaches are on the rise and are expensive. According to the Cost of a Data Breach Report 2020 by IBM based on research conducted with 524 organizations worldwide, the following key-findings will intrigue you:

  • The malicious attack contributes to more than 50% of the data breach root cause, and it is the most expensive one.
  • The average total cost of a data breach in the US has been estimated to be $7.9 million, and $3.86 million on average worldwide. (10% increase since 2014)
  • Data breaches are becoming harder to track and locate. In 2020, the average amount of time to identify that a breach had occurred was 280 days.

The more straightforward solution is to try to prevent breaches before they can occur; therefore, authentication and authorization are critical.

What is Authentication?

Authentication confirms your identity. It is a process that proves that you are the person who you say you are. In the digital world, the most common way to authenticate is to use a username and password. For example, while logging into your eCommerce account, you prove your identity by providing an email and a password, sometimes followed by an OTP or a second factor. There are many other ways to authenticate, and these are depicted in the figure below.

Common Methods for Authentication

Common Mechanisms for Authentication

  • Token-based authentication — It is a temporary credential that you (client) have which proves your identity. Read more here. Note: we do use this later in the form of JWT, but we first authenticate with the username and password, then send the token on each subsequent request.
  • OAuth — is an industry-standard protocol for authorization that provides a token on your behalf once you’ve authenticated to the external service. Read more here. For example, if you’ve ever seen a “Sign in with Google/Facebook/etc”, this is likely done using OAuth.
  • Time-based token (TOTP) — a token is generated with something only you know. This token changes after some time period. Read more here. This is very commonly used in Two Factor Authentication as the 2nd factor. If you have random codes you need to enter, they may have been generated using this scheme.
  • Biometric authentication such as Fingerprint or Facial recognition (popular on smartphones). Most of the standard mechanisms for authentication utilizes either of the following two “Network authentication protocols”: i). Kerberos, and ii). Secure Sockets Layer (SSL) / Transport Layer Security (TLS).

More to Explore — Network Authentication Protocols

Kerberos provides secure authentication for client/server applications by using secret-key cryptography. Here are some links on Kerberos here. On the other hand, SSL/TLS uses a cryptographic system that uses two keys to encrypt data − a public key known to everyone and a private or secret key known only to the recipient of the message. A typical example is an HTTPS website that utilizes SSL/TLS certificate that is signed by a publicly trusted Certifying Authority. Here is a resource for SSL/TLS here.

Details about these “Network authentication protocols” are beyond the scope of this lesson.

Authentication Considerations

Good authentication practices center around protecting the data used for authentication, such as:

  • You wouldn’t just tell people your password, and so your website shouldn’t either.
  • You also need to ensure that passwords are sufficiently random and complex, with things like length requirements, to ensure that your credentials aren’t easily cracked.
  • They must not be able to be forged easily, so no one can impersonate anyone else. Finally, they have to be deterministically generated, that is they must be generated the same way every time, to ensure you actually can authenticate users into your system.

As referenced in the above video, you can check out the OWASP authentication cheatsheet for guidelines to authentication best practices. This is a great resource to check out now, or to bookmark for later reference.

1. What is Hashing?

Hashing is the process of generating a unique value (hash) for a given text, string, or numeric input (key). The generated value (called a hash) itself could be either a text, string, or numeric, which depends upon the underlying Hash function.

A Hash function is a one-way mathematical function which is used to generate a unique value for a given input.

The two essential properties of hashing are:

  • Hashing must be irreversible
  • Each input should have a unique output (or, practically, as close to unique as possible)

A scenario, when a hash function gives the same output for different inputs, is called a collision.

Irreversible vs. Reversible functions

Let there be a function f, which can generate unique hash values for a given set of input. Another function g can get the original value back if the hash value is given as input. In such a case, the function f would be called reversible, as we can get the original value back. Hence, f cannot be used as a Hash function. Following is an example of a reversible function:

The hashes should be irreversible, so one cannot compute the input given the output.

2. Where to use Hashing in an Application?

We use hashing to store any sensitive information in the system, such as user passwords.

Theoretical Example

Let’s look at a simple example of hashing. Suppose that a user has the following password:

  • passw0rd! - If we directly store this text in the database, we will have a major security vulnerability.

So instead, we can first run the password through a one-way function that produces a jumbled up piece of text (which has no obvious discernible relationship to the original password). That jumbled up piece of text is the hash, and it might look something like this:

  • passw0rd! → hash function → @kdF3lkAWoLA

So when the client interacts with the server, rather than directly sending the password, the client can instead send the hash:

  • passw0rd! → hashing function → @kdF3lkAWoLA → stored in database

This way, if someone gains access to the database, they will still not have access to the plain-text password.

3. How to Pick a Good Hash Function

A good hash function needs to be efficiently computable, so it needs to be reasonably fast. It needs to be uniform, which means given an input the output needs to be as unique as possible. In other words, a low number of collisions exists. A given output should give absolutely no indication of its input. Inputs should be effectively random and uniformly distributed. Changing “cat” to “bat” should yield unpredictable results (this is known as the avalanche property)

Some famous Hashing Algorithms

There are many hashing algorithms prevalent in the industry.

  • MD5: The MD5 Message-Digest Algorithm is a hash function that accepts an input message of any length, and correspondingly produces a 128-bit (16-byte) hash value. Mostly, MD5 is used to verify data integrity. It was proposed by Ronal Rivest in 1992, as specified in RFC 1321. MD5 is comparatively unsafe, as it might get reversed by using brute-force-attack. Also, the chances of collision are very high in MD5. For non-critical applications, MD5 can be a good choice as it is computationally faster than other algorithms.
  • SHA: The SHA (Secure Hash Algorithm) is a set (SHA-0, SHA-1, SHA-2, and SHA-3) of cryptographic hash functions developed by the National Institute of Standards and Technology (NIST). In comparison to MD5, SHA generates secure hashes. SHA-1 is a 160-bit hash function. SHA-2 is further of two types: SHA-256 and SHA-512. SHA-256 is a 256-bit hash function that provides 128 bits of security in the case of collision attacks, while SHA-512 is a 512-bit hash function is designed for 256 bits of security. SHA-3 supports the same hash lengths as SHA-2. Chances of collision are high in SHA as well, but lesser than MD5. Thus, SHA-2 could be a good choice for general purpose application with a limited set of inputs, such as a University portal.
  • bCrypt: It is generally used to generate the hash for user-passwords. bCrypt is based on the Blowfish cipher algorithm. It has a crucial phase for key setup. This phase starts with encrypting a sub-key and uses the output of this encryption to change another sub-key. This way, the bCrypt involves iterative steps for generating the hash, making it a preferred choice of developers for critical applications.
  • sCrypt: It is a computationally intensive password-based key derivation function, proposed in 2016, as specified in RFC 7914. As part of the algorithm, it generates a large vector of pseudorandom bit strings. Thus, it requires a large amount of memory for computation. It isn’t easy for a brute-force-attacker to reverse the hash, as it would involve a significantly high amount of time, memory, and a high number (billion) of attempts. Other password-based key derivation functions such as PBKDF1 and PBKDF2 have relatively low resource demands.

4. Explore Further — Collision

In several scenarios, two different keys can generate the same hash. Such a scenario is called Collision. If we use a simple hash function, such as input length or sum of ASCII code of all characters, then it might lead to a collision. The diagram below lists the approaches used for collision resolution.

Collision Resolution Techniques

A collision can be resolved by using any of the following techniques:

  1. Separate Chaining — It is a type of Open Hashing technique. The idea is to store the keys corresponding to collision (same) hash outputs in a Linked List. There would be a separate Linked List for each unique hash output.
  2. Open Addressing — It is also called Closed Hashing. In this approach, for a given set of $n$ input keys, we take a data structure that can accommodate more than $n$ keys. The idea is to store the keys corresponding to collision (same) hash outputs in the next available slot in the data structure.
  • Linear or quadratic probing — Keep probing until an empty slot is found.
  • Double Hashing — We use two hash functions — one for hashing, and another for calculating the offset. Then, this offset is appended to the output of the first hash function. This way, the final output is expected to be a collision-free value.

You may find it useful to read further about Collision Resolution Techniques here.

Salting

A salt is random data that is used as an additional input to the hash function so that the final hash becomes more secure.

Salting is an approach to generate two different hash values for two different users providing the same input.

How does the Salting Works?

The following figure shows another example to explain the concept of Salting:

Example — Two users providing the same password, would generate a same hash. However, the salting will generate a different hash.

Points to Consider

  1. In a web application, the Salting must be done on the Server.
  2. While hashing user-passwords, the Salt should be generated randomly. It is preferable if the Salt is unique for each user’s password.
  3. For numeric Salt, it is good to use secure algorithms such as Cryptographically Secure Pseudo-Random Number Generator (CSPRNG). Java has java.security.SecureRandom class for generating PRNG.
  4. For pseudo-random alpha-numeric string generator, you may use Apache class, as org.apache.commons.text.RandomStringGenerator
  5. When we use Salting, there are two separate steps involved -
  • Generate the salted password, and
  • Verify the salted password.

We would see the detailed implementation in the project, where we would implement the bCrypt hashing algorithm along with Salting.

Implement Hashing (SHA-256)

MessageDigest class

The java.security.MessageDigest class provides us an easy implementation of the MD2, MD5, SHA-1, SHA-256, SHA-384, and SHA-512 algorithms. The following example shows the SHA-256 implementation by creating an instance of the MessageDigest class. For more details of the MessageDigest class, refer here.

SecureRandom class

Salting is done by using an instance of the java.security.SecureRandom class. Let's look at the steps:

Step 1 — Create a main method to call the custom createSalt() and get_SecurePassword() methods

//Necessary imports
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.MessageDigest;
public class SaltExample {public static void main(String[] args)throws NoSuchAlgorithmException, NoSuchProviderException {
// A static String for the example
String passwordToHash = "myPassword";
// Create a salt
byte[] salt = createSalt();
// Create a hash
String securePassword = get_SecurePassword(passwordToHash, salt);
System.out.println(securePassword);
}
}

Step 2 — Create the createSalt() to return a byte array

// Method to generate a Salt
private static byte[] createSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return salt;
}

Step 3 — Create the get_SecurePassword() to return a String

// Method to generate the hash. 
//It takes a password and the Salt as input arguments
private static String get_SecurePassword(String passwordToHash, byte[] salt){
String generatedPassword = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] bytes = md.digest(passwordToHash.getBytes());
StringBuilder sb = new StringBuilder();
for(int i=0; i< bytes.length ;i++)
{
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
generatedPassword = sb.toString();
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return generatedPassword;
}

Implement Hashing (bCrypt)

BCryptPasswordEncoder class

In our upcoming demo, we will demonstrate the use of bCrypt technique for hashing in our Spring Boot project. Fortunately, you do not have to implement it from scratch, instead, you can use a library class org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder , which is a part of the Spring framework.

Step 1 — Add the dependency
Add the Spring Boot Starter Security Maven dependency to the pom.xml

Step 2 - Create an instance of the BCryptPasswordEncoder class
You can create an instance, and call any of the built-in methods.

BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();  // Call a built-in method
String securePassword = bCryptPasswordEncoder.encode("mySaltedPassword");

In the code snippet above, securePassword is the generated hash, and the mySaltedPassword is the String representing the raw password.

Note — We will demonstrate the complete workflow in the upcoming demo towards the end of the lesson.

Step 3 — Call either of the following built-in methods of the BCryptPasswordEncoder class

  • encode() method - Encodes the raw password. Generally, a good encoding algorithm applies an SHA-1 or greater hash combined with an 8-byte or greater randomly generated salt.
// To encode a given rawPassword
public String encode(CharSequence rawPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
} else {
String salt;
if (this.random != null) {
salt = BCrypt.gensalt(this.version.getVersion(), this.strength, this.random);
} else {
salt = BCrypt.gensalt(this.version.getVersion(), this.strength);
}

return BCrypt.hashpw(rawPassword.toString(), salt);
}
}
  • matches() method - It matches and verifies the encoded password obtained from the storage, and the submitted raw password (after encoding). Returns true if the passwords match, false if they do not. The stored password itself is never decoded.
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
} else if (encodedPassword != null && encodedPassword.length() != 0) {
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
this.logger.warn("Encoded password does not look like BCrypt");
return false;
} else {
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
} else {
this.logger.warn("Empty encoded password");
return false;
}
}
  • upgradeEncoding() method - It returns true if the encoded password should be encoded again for better security, else false. The default implementation always returns false.
public boolean upgradeEncoding(String encodedPassword) {
if (encodedPassword != null && encodedPassword.length() != 0) {
Matcher matcher = this.BCRYPT_PATTERN.matcher(encodedPassword);
if (!matcher.matches()) {
throw new IllegalArgumentException("Encoded password does not look like BCrypt: " + encodedPassword);
} else {
int strength = Integer.parseInt(matcher.group(2));
return strength < this.strength;
}
} else {
this.logger.warn("Empty encoded password");
return false;
}
}

Authorization

What is Authorization?

The authorization concerns itself with permission and rights.

In general, Authorization comes after Authentication. Authorization determines whether you are permitted or have the right privilege to access the requested resources. In other words, it determines what a user is allowed to access.

The following figure shows the order of execution of basic authentication and authorization in an application that follows the Model View Controller (MVC) architectural pattern.

Basic authentication and authorization steps

Role-Based Access Control (RBAC)

In RBAC, access is given based on a user’s role — as a manager, engineer, customer service representative, etc.

Permissions can then be given (and limited) to users based on their roles. That way, each type of user only has limited access — they are able to access only the specific things they need for their particular job.

How to bring Authentication and Authorization together?

Authentication and authorization are different, yet related. You can’t grant a right to a user (i.e., authorize that user) without first knowing who that user is (i.e., by authenticating their identity).

JWTs

JSON Web Token (JWT) is an open standard RFC 7519, that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

  • JWTs are used for authentication in (RESTful) microservices architecture.
  • REST is an acronym for REpresentational State Transfer. It is a software-architectural style, in which there is stateless communication between client and server.
  • Stateless means that the server does not have to store the user cookies or other session data for any incoming request. Rather, the server authenticates a user based on a token (JWT).

Example — An example of JSON Web Token is shown below. Can you notice the three parts (a header, payload, and signature) separated by a period (.)?

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJVZGFjaXR5X1Rlc3QiLCJleHAiOjE2MDgzNjYxNDJ9.0kjTgAOOJkNxM908qJE9p1503-mi-2tha9UVneI04EXvxte4KpKYl0pVMjXG-FLtDFzfmEuRRYyPm2JRGvlKFg

The token above is a Base64 encoded form of the individual three parts.

These three parts together help to authenticate a user.

  1. Header — In the example above, the eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9 represents the header, such as:
{
"alg": "HS256",
"typ": "JWT"
}
  1. Payload — The eyJzdWIiOiJVZGFjaXR5X1Rlc3QiLCJleHAiOjE2MDgzNjYxNDJ9 is the payload, also called a Claim. After Base64 decoding, you can get the actual user-data, such as:
{
"sub": "1234",
"name": "Sareeta Panda",
"admin": true
}

The user-data above is not necessarily secret because anyone who has access to the token can decode (Base64 decoding) the payload. The payload tells us the username/ID (not password) of the user, i.e., who is making the request.

  1. Signature — The remaining third part is the signature, which helps the server to verify the authenticity of the token as well as the user. In fact, the signature is generated by using the header, payload, and a secret residing on the server only. If anyone (header, payload, and a secret) changes, then the signature will also change.

How does it work?

The diagram below shows how to generate, and use a JWT.

Two steps while using the JWT for authentication

  • Step 1 — Generate JWT — A user can attempt to log in from any client. The server returns a JSON Web Token (JWT) upon successful validation of the user credentials. This JWT is then stored locally in the client.
  • Step 2 — Use JWT — Later, when the user requests to access any protected resource, a JWT is sent along. Then the server performs the JWT validation before granting access to the resource.

What is statelessness?

JSON web tokens are intrinsically stateless, meaning, the server does not store the user’s session or cookies. When a JWT is sent back from a client to a server, the server only has to validate the token.

Statelessness also solves the problem of scalability. Let’s say we have our API server that starts to have a tremendous amount of demand. In this case, we’ll be spinning up multiple servers over the same service.

Now, a JWT could be hitting any one of these servers, and since it’s stateless, each of those servers can be confident in the identity provided.

Additional Resources

  • JWT.io a useful guide and list of popular JSON Web Token implementations.
  • Base64 Encoding
  • HMAC keyed-hash message authentication code

Demo: eCommerce Starter Code

Let’s walk through the given starter code of the e-Commerce application, and then we will implement the JWT. We’ll be working with the eCommerce application throughout this course, and then — at the end of the course — you’ll finish and submit the application as your final project.

Step 1: Fork, and Clone the Project starter code

Note — To make it convenient for you, we have created a new Github repo dedicated to this course. All the steps mentioned above will be the same, except the repo name, and the folder name.

The repository for the eCommerce application starter code can be found at: https://github.com/udacity/nd035-c4-Security-and-DevOps . Did you notice that this repo contains multiple branches, that contain the code associated with upcoming demos and exercises?

Switch to a different branch as you progress through this course.

Each branch contains an individual Maven project for you to follow.

How to work with Github repo?

You can choose from either of the below two options:

  1. Option — Clone the repo locally
    Here are the steps that you need to follow:
  • Fork the repo to your account.
  • Clone the repo using the command in your terminal (Linux/MacOS) or Git Bash (Windows):

git clone https://github.com/USERNAME/nd035-c4-Security-and-DevOps.git

  • Import the project into the IntelliJ IDE.
  • Switch between branches, and make changes. By default, you will be on the master branch. To switch branch, use the commands:
git branch
git checkout <branch-name>

After switching the branch, the code in the IntelliJ will also change automatically.

  • Push the local changes back to the remote (online) repo in your account. You can use:
git add -A 
git commit -m "your message"
git push origin <branch-name>git add -A git commit -m "your message" git push origin <branch-name>

2. Option — Link your IntelliJ IDE to Github repo (Recommended)
You can even connect your IntelliJ directly with the Github repo (in your account), and switch branches when needed. Moreover, you can perform all Github operations from the IntelliJ IDE. Refer to the Troubleshooting Tips page for more info.

Step 2: Code walkthrough and making requests to the API endpoints from the Postman tool

Class Diagrams

Let’s have a look at various classes present in the starter code.

Classes in the controllers package

Classes in the model/persistence/ package. In addition, it has a repositories sub-package.

Classes in the model/persistence/repositories/ package

Classes in the model/requests/ package

Demo: Authentication and Authorization using JWT

Solution

The complete code followed in the video above is available in the 1.Auth branch of the course Github repository.

Facing difficulty while following the video above or have issues with the new branch? Refer to the Troubleshooting Tips on the next page.

Add the Maven dependency

Add the Java JWT Maven dependency to your pom.xml

<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.11.0</version>
</dependency>

Note: The version may vary in the demo video above, and the current version available.

Refer to the list of available algorithms, and the usage (create, verify, decode a token) of the above library in the README here.

Create the security package

As demonstrated in the video above, create the src/main/java/com/example/demo/security folder and the Java file to place in that folder are available for view/download at the bottom of the current page.

Note — If you download the Java files attached below, then the system will automatically change the name (letter-case) of the file.

The details of the Java files are explained below:

1. JWTAuthenticationFilter.java

This custom class is responsible for the authentication process. This class extends the UsernamePasswordAuthenticationFilter class, which is available under both spring-security-web and spring-boot-starter-web dependency. The Base class parses the user credentials (username and a password). You can have a look at all the available methods of the Base class here.

We have overridden the following two methods:

  1. attemptAuthentication() - It performs actual authentication by parsing (also called filtering) the user credentials.
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User credentials = new ObjectMapper()
.readValue(req.getInputStream(), User.class);

return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
credentials.getUsername(),
credentials.getPassword(),
new ArrayList<>()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
  1. successfulAuthentication() - This method is originally present in the parent of the Base class. After overriding, this method will be called after a user logs in successfully. Below, it is generating a String token (JWT) for this user.
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {

String token = JWT.create()
.withSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
.sign(HMAC512(SecurityConstants.SECRET.getBytes()));
res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
}

2. SecurityConstants.java

This class contains the literal constants that are used in the JWTAuthenticationFilter class.

3. JWTAuthenticationVerficationFilter.java

This class is responsible for the authorization process. This class extends the BasicAuthenticationFilter class. It overrides on method, and defines another custom method.

  1. Custom method — getAuthentication() - It validates the token read from the Authorization header.
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req) {
String token = req.getHeader(SecurityConstants.HEADER_STRING);
if (token != null) {
String user = JWT.require(HMAC512(SecurityConstants.SECRET.getBytes())).build()
.verify(token.replace(SecurityConstants.TOKEN_PREFIX, ""))
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}

2. Overridden method — doFilterInternal()- This method is used when we have multiple roles, and a policy for RBAC.

@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws IOException, ServletException {
String header = req.getHeader(SecurityConstants.HEADER_STRING);

if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}

UsernamePasswordAuthenticationToken authentication = getAuthentication(req);

SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}

4. WebSecurityConfiguration.java

After defining the authentication and authorization modules, we need to configure them on the Spring Security filter chain. The WebSecurity class is a custom implementation of the default web security configuration provided by Spring Security. In this class, we have overridden two overloaded methods:

  1. configure(HttpSecurity) - Defines public resources. Below, we have set the SIGN_UP_URL endpoint as public. The http.cors() is used to make the Spring Security support the CORS (Cross-Origin Resource Sharing) and CSRF (Cross-Site Request Forgery). Read more here.
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthenticationVerficationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

2. configure(AuthenticationManagerBuilder) - It declares the BCryptPasswordEncoder as the encoding technique, and loads user-specific data.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManagerBean())
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}

5. UserDetailsServiceImpl.java

It implements the UserDetailsService interface, and defines only one method that retrieves the User obejct from the database:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), Collections.emptyList());
}

Recommended Read

Conclusion

If you have been following along, now you must be able to …

  • Explain the fundamentals of authentication and authorization
  • Implement the hashing and salting techniques, such as bCrypt, and SHA-256
  • Make use of JSON Web Tokens (JWTs) for authentication in your Spring application

Supporting Materials

Troubleshooting Tips

  1. Dependency added in the pom.xml yet the IDE shows package not found error when rebuilding the project.
    If you face any error in your pom.xml about unresolved packages, try File → Invalidate Caches and Restart option in your IntelliJ IDE. Also, note that the order of the dependencies in the pom does matter.
  2. Cannot see the project folders in IntelliJ IDE
    When you import a project into the IDE (maybe from VCS), there is a possibility that you may not be able to see the project structure. In such cases, you just need to close the IDE and remove the .idea folder from the current project directory.
  3. How do I change the Java SDK version for the current project or the individual module?
    You can verify/change the Java SDK version for your project by going to the following menu-option in the IDE:

Snapshot: Verify or change the Java SDK version

4. How do I change the language level for a particular Module?
Go to the Project Structure, and view/edit the language level. The language level must be the same as the SDK version chosen for the project.

Snapshot: Set/verify the language level for the individual Module

5. How do I change the default Java compiler for all projects?
You can go to the following option in the IDE to view/set the Java compiler version:

Snapshot: View/change the Java compiler version

6. How do I import a project from Github into the IntelliJ IDE?
You can import a project available in a Github repository (or any of its branches) directly into your IDE. You can even perform all standard operations within your IDE, such as, add, commit, push, pull, merge, switch branch, etc. For more info, refer here.

  • Note — If the imported project is not shown as a Maven project, simply right-click on the pom.xml file, and choose to add it as a Maven project.

Add the link to the Github repository in the VCS

Perform all standard Github operations, using the VCS menu

VCS operations pop-up. Use this option to switch between Github branches. Don’t forget to periodically (invalidate cache and) restart your IDE.

7. My issue is not listed above
Refer to the Troubleshooting common Maven issues — official guide.

--

--

Sol

SAP FI Developer dedicated to business growth 📈