1. Introduction

1.1. Global overview

This exercise contains two steps
  • Implementing a very simple security using hard coded username and password to get it started

  • Add real DB username and password

1.2. Assumptions

Some assumptions
  • you are used to create normal package names

    • nl.acme.applicationName.repository for the repositories

    • nl.acme.applicationName.service for the services

  • you are aware of JPA and know how to create an @Entity

  • You know what Maven is

  • You know how to handle your database whether MySQL or H2

1.3. Problems you might run into

securedEnabled should be set to true in @EnableGlobalMethodSecurity
  • @EnableGlobalMethodSecurity in SecurityConfig should be changed to ⇒

  • @EnableGlobalMethodSecurity(securedEnabled = true)

Source of SecurityConfig BEFORE change
1
2
3
4
5
6
7
8
9
@Configuration
@EnableGlobalMethodSecurity // <= !!!!!!!!!!!!!!!!!!!!!!!!
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    public static final String API = "API";
        .
        .
        .
Source of SecurityConfig AFTER change
1
2
3
4
5
6
7
8
9
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true) // <= !!!!!!!!!!!!!!!!!!!!!!!!
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    public static final String API = "API";
        .
        .
        .
Password encoding
  • Symptom: Something like there is no password encoding

  • Cause: Since Spring Security 5 something has changed using password encoding

  • Fix: Add the following line to the SecurityConfig class his configureGlobal method

    • passwordEncoder(NoOpPasswordEncoder.getInstance())

So it looks like this
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
                            @Value("${api.username}") String user,
                            @Value("${api.password}") String pass) throws Exception{

    auth.inMemoryAuthentication()
            .passwordEncoder(NoOpPasswordEncoder.getInstance()) // !!!
            .withUser(user)
            .password(pass)
            .roles(API);
Unable to start the H2-console
  • Symptom: When starting the H2-console the screen appears to be blank

  • Cause: FrameOptions not set

  • Fix: Add the following line to the SecurityConfig::configure method

    • httpSecurity.headers().frameOptions().disable();

So it looks like this
.
.
.
@Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                // we don't need CSRF because our token is invulnerable
                .csrf().disable()
                                ...
                                ...
                                ...
            // for using the H2 Console
            httpSecurity.headers().frameOptions().disable(); // <= Add this line!!!!!!!!!!!!!!!!!!
    }
Some imports are missing for brevity
  • org.springframework.security

  • org.slf4j

2. Topic: Simple Security

2.1. Add Maven dependency for introducting Spring Security

Add this to pom
                <!-- for using Spring Security -->
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-security</artifactId>
                </dependency>
Be aware that after this addition in pom.xml the application will not behave normal anymore

2.2. Implement SecurityConfig to have a simple REALM

In fact we are creating a REALM now; which is a user and password database

Create the following SecurityConfig.java file in a package nl.acme.applicationName.config You might copy and paste this code. (What else should you) but you MUST know that you are creating a realm and stating that some URLs are not open anymore.

Add SecurityConfig.java in config package
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;

@Configuration
@EnableGlobalMethodSecurity
@EnableWebSecurity
// see
// http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#multiple-httpsecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

        public static final String API = "API";

        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth,
                        @Value("${api.username}") String apiUsername,
                        @Value("${api.password}") String apiPassword) throws Exception{

                        auth.inMemoryAuthentication().withUser(apiUsername).password(apiPassword).roles(API);
        }

        @Configuration
        @Order(1)
        public static class AuWebSecurityAdapterRest extends WebSecurityConfigurerAdapter {

                @Override
                protected void configure(HttpSecurity httpSecurity) throws Exception {
                        httpSecurity.csrf().disable();
                        httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                        httpSecurity.authorizeRequests().antMatchers("/api/**").hasRole(API).and().httpBasic();

                        httpSecurity.authorizeRequests().anyRequest().permitAll();

                        // the rest is implicit denied
                }
        }

}

2.3. Add user and password to application.properties

Add this to the (appropriate) application.properties
api.username=<choose your username>
api.password=<choose your password>
you should remote the < and > brackets also

2.4. Validating

In Postman try to access your endpoint which should fail

The trainer should demo you the Postman settings for getting username and password in but if you are impatient :-) you might use it like this

postman

3. Topic: Advanced Security

3.1. Add a domain class for the User

Add the following file to your model package
  @Entity
  public class User  {

        @Id
        @GeneratedValue
        private long id;

    private String username;
    private String password;

    // add getters and setters (except setId)
  }
Create a repo for fetching DB stuff
@Repository
public interface UserRepository extends CrudRepository<User, Long>{

        Optional<User> findByUsernameAndPassword(String username, String password);

}

3.2. Create a UserService

Create this
@Service
public class UserService {

        @Autowired
        private UserRepository repository;

         public boolean authenticate(String username, String password) {
        return this.repository.findByUsernameAndPassword(username, password).isPresent();
    }

3.3. Create a UserAuthenticationProvider

Create this file somewhere in a package called security e.g. nl.acme.applicationName.security

This class is responsible for authentication a User
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {

        private static final Logger LOGGER = LoggerFactory.getLogger(UserAuthenticationProvider.class);

        @Autowired
        private UserService userService;

        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                final String username = authentication.getName();
                final String password = authentication.getCredentials().toString();

                        if (this.userService.authenticate(username, password)) {

                                List<GrantedAuthority> grantedAuths = new ArrayList<>();
                                grantedAuths.add(new AuthenticationTokenGrantedAuthority());

                                LOGGER.debug("Validation succeeded for [{}]!", username);

                                return new UsernamePasswordAuthenticationToken(username, password, grantedAuths);
                        }
                        else {
                                LOGGER.error("Validation failed for [{}]", username);
                                throw new AuthenticationTokenAuthenticationException(String.format("Validation failed for %s", username));
                }
        }

        @Override
        public boolean supports(Class<?> authentication) {
                return authentication.equals(UsernamePasswordAuthenticationToken.class);
        }

}

// this is a class in the same file as the above created UserAuthenticationProvider
class AuthenticationTokenAuthenticationException extends AuthenticationException {

        private static final long serialVersionUID = -9139561336028106640L;

        public AuthenticationTokenAuthenticationException(String msg) {
                super(msg);
        }
}

class AuthenticationTokenGrantedAuthority implements GrantedAuthority {

        private static final long serialVersionUID = -4893914488018936401L;

        @Override
        public String getAuthority() {
                return "ROLE_"+SecurityConfig.API;
        }
}

3.4. Finally wire the UserAuthenticationProvider in the SecurityConfig

In SecurityConfig add the field authenticationProvider
        @Autowired
        private UserAuthenticationProvider authenticationProvider; // <=

    public static final String API = "API";

The parameters annotated with @Value in SecurityConfig::configureGlobal can now be removed. You can also remove the api.username and api.password entries from your application.properties file

In the method configureGlobal(…​) in SecurityConfig remove the ENTIRE body of the method

In the method configureGlobal(…​) in the class SecurityConfig add the following line to the body of the method

auth.authenticationProvider(authenticationProvider);
So that …​ finally …​ your SecurityConfig contains this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import nl.capgemini.divingweb.security.UserAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;

// see
// http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#multiple-httpsecurity

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    public static final String API = "API";

    @Autowired
    private UserAuthenticationProvider authenticationProvider;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.authenticationProvider(authenticationProvider);
    }

    @Configuration
    @Order(1)
    public static class AuWebSecurityAdapterRest extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity.csrf().disable();
            httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
            httpSecurity.authorizeRequests().antMatchers("/api/**").hasRole(API).and().httpBasic();

            httpSecurity.authorizeRequests().anyRequest().permitAll();
            // the rest is implicit denied
        }
    }
}

3.5. Adding users to the database

Using your knowledge of MySQL just insert a username and password the the table User of your database of create a RestController which is able to insert a User using a POST to api/users

3.6. Validating

The REST service should now normally respond like the first part of this exercise

4. Topic: Using Bcrypt

4.1. Introduction

In the previous section we learned that we can save or username and password in plain text. That was OK for that moment. In the wild it would not suffice. In the wild the saving of the password should be hashed.

The fixing of that problem will be the subject of this section

4.2. What you will learn

In the next section of this tutorial you will learn
  • How to create a bean for using for PasswordEncoding

  • How to create a bean for using for DaoAuthentication

  • How to add a UserService which implements UserDetailsService for

    • saving users with password encoding

    • fetching a user

4.3. Why: Using Bcrypt

To prevent that a person with criminal intent can read the password directly from the database

4.4. When: Using Bcrypt

When you want to prevent that a person with criminal intent can read the password directly from the database - and that is always in the wild

4.5. What: Using Bcrypt

Get a big cup of coffee and Read this to get informed regarding Bcrypt

4.6. How: Using Bcrypt

Below you find the steps to take to get the passwords hashed with Bcrypt

First, let us create the beans necessary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package nl.capgemini.divingweb.config;


import nl.capgemini.divingweb.service.security.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class MyBeans {

    @Autowired
    private UserService userService;


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Bean
    public DaoAuthenticationProvider authenticationProvider(@Autowired PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();

        authProvider.setUserDetailsService(this.userService);
        authProvider.setPasswordEncoder(passwordEncoder);

        return authProvider;

    }
}
Explanation of the code above
  • 12: @Configuration: to tell Spring Boot that he has to look here (just like @Repository and @RestController)

  • 15: The wired in UserService

  • 19: the passwordEncoder; THE BEAN where this story is all about

  • 25: the DaoAuthenticationProvider. The bean used to be used as the authenticationProvider WHICH NOW uses the userDetailsService!!!

Change SecurityConfig
  • change the field::UserAuthenticationProvider to DaoAuthenticationProvider

So that the top of SecurityConfig looks like this
1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    public static final String API = "API";

    @Autowired
    private DaoAuthenticationProvider authenticationProvider;

        .
        .
        .
For the time being remove the role authorization and change it to authentication only
  • change this line: httpSecurity.authorizeRequests().antMatchers("/api/**").hasRole(API).and().httpBasic();

  • to this line: httpSecurity.authorizeRequests().antMatchers("/api/**").authenticated().and().httpBasic();//hasRole(API).and().httpBasic();

Why?
  • To have username and password running first and later add roles again

Add the findByUsername to the UserRepository
1
2
3
4
5
6
7
@Repository
public interface UserRepository extends CrudRepository<User, Long> {

   Optional<User> findByUsernameAndPassword(String username, String password);

   User findByUsername(String username); // <=
}

Remove the UserAuthenticationProvider AND HIS inner classes!!!!!!!!!!!

Changes to the UserService
  • Autowire the passwordEncoder in the UserService

  • Add a @PostConstruct method to the UserService to have one or more users when starting

  • UserService should implement UserDetailsService

    • implement the UserDetailsService interface method loadUserByUsername(String username) to the UserService

So, finally, the UserService looks like this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package nl.capgemini.divingweb.service.security;

import nl.capgemini.divingweb.model.security.User;
import nl.capgemini.divingweb.persistence.security.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
public class UserService implements UserDetailsService {


    private static final String dummyUser = "john";
    private static final String dummyPassword = "doe";

    @Autowired
    private UserRepository repository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @PostConstruct
    public void setUp() {

        Optional<User> optionalUser = this.repository.findByUsernameAndPassword(dummyUser, dummyPassword);
        if(!optionalUser.isPresent()) { // OK insert him
            User user = new User();
            user.setUsername(dummyUser);
            user.setPassword(passwordEncoder.encode(dummyPassword));
            this.repository.save(user);
        }

    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {


        User user = this.repository.findByUsername(username);

        UserDetails userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());

        return userDetails;
    }
}

4.7. Assignment: Using Bcrypt

Target

To learn working with BCrypt hashing in a Spring Boot application

Roadmap

During this assignment you will add BCrypt hashing to the user authentication process

Steps
  • Given the code above

  • Implement it to the application

  • Run the app and try to login using Postman with username:john and password:doe

4.8. Conclusion

During this section we learned how to make use of BCrypt so that we have a secure security model In the following section we will learn that we can (also) make use of Role(s) to authorize a request

4.9. Follow-up: Using Bcrypt

Below you find for this topic some extra resources to watch after the week the topic Using Bcrypt is introduced during the training

Youtube movies
  • movie 1

Links
  • link one

5. Topic: Add Authorities

5.1. Introduction

In the previous section we learned that we can sign on using a username and password and we use a database for authorization.

In the wild we also want to use Roles (called Authorities) to authorize a request for some url

Using Authorities with a Database oriented authentication scheme is the subject of this section

5.2. What you will learn

In the next section of this tutorial you will learn
  • How to setup the Authorities for autorisation

5.3. Why and When: Add Authorities

When you want to authorise based on a kind of Role / Authority and not only based on username and password

5.4. What: Add Authorities

What authorities are
  • An authority is a relationship (using JPA) from the User table to the table Authority

5.5. How: Add Authorities

Create an enum AuthorityName
1
2
3
4
5
package nl.capgemini.divingweb.model.security;

public enum AuthorityName {
  USER, API
}
Create an entity class Authority
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package nl.capgemini.divingweb.model.security;

import org.springframework.security.core.GrantedAuthority;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;

@Entity
public class Authority implements GrantedAuthority {

   @Id
   @GeneratedValue(strategy= GenerationType.AUTO)
   private Long id;

   @NotNull
   @Enumerated(EnumType.STRING)
   private AuthorityName name;

   @ManyToMany(mappedBy = "authorities", fetch = FetchType.EAGER)
   private List<User> users = new ArrayList<>();

    public Long getId() {
       return id;
   }

   public void setName(AuthorityName name) {
       this.name = name;
   }

   public List<User> getUsers() {
       return users;
   }

    @Override
    public String getAuthority() {
        return this.name.toString();
    }
}
Explaination of the code above
  • 11: The class which we are going to use for the Authority should implement the interface GrantedAuthority which makes the implementation of String getAuthority() mandatory

  • 18: The AuthorityName name which is an enum should be persisted as String and not the ordinal number of the enum

  • 21 and beyond: The Authority has some users

Create an AuthorityRepository
1
2
3
4
5
6
7
8
9
package nl.capgemini.divingweb.persistence.security;

import nl.capgemini.divingweb.model.security.Authority;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface AuthorityRepository extends CrudRepository<Authority, Long> {
}
Create an AuthorityService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package nl.capgemini.divingweb.service.security;

import nl.capgemini.divingweb.model.security.Authority;
import nl.capgemini.divingweb.persistence.security.AuthorityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AuthorityService {

    @Autowired
    private AuthorityRepository authorityRepository;

    public Authority save(Authority a) {
        return this.authorityRepository.save(a);
    }
}
Create an entity class User
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package nl.capgemini.divingweb.model.security;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String username;
    private String password;

    @ManyToMany(fetch = FetchType.EAGER)
    private List<Authority> authorities = new ArrayList<>();


    public long getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    public List<Authority> getAuthorities() {
        return authorities;
    }

    public void setAuthorities(List<Authority> authorities) {
        this.authorities = authorities;
    }
}
Explaination of the code above
  • Simply said, the inverse relationship of the Authority which is mentioned previously

Modify the UserService
  • Autowire the AuthorityService in the UserService

  • Modify the setUp method for having some users to persist the Authority for the user

  • In the loadUserByUsername method set the corresponding Authorities for the user

So that your UserService now looks like this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package nl.capgemini.divingweb.service.security;

import nl.capgemini.divingweb.model.security.Authority;
import nl.capgemini.divingweb.model.security.AuthorityName;
import nl.capgemini.divingweb.model.security.User;
import nl.capgemini.divingweb.persistence.security.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Optional;

@Service
public class UserService implements UserDetailsService {


    private static final String dummyUser = "john";
    private static final String dummyPassword = "doe";

    @Autowired
    private UserRepository repository;

    @Autowired
    private AuthorityService authorityService; // <==

    @Autowired
    private PasswordEncoder passwordEncoder;

    @PostConstruct
    public void setUp() {

        Optional<User> optionalUser = this.repository.findByUsernameAndPassword(dummyUser, dummyPassword);
        if(!optionalUser.isPresent()) { // OK insert him
            User user = new User();
            user.setUsername(dummyUser);
            user.setPassword(passwordEncoder.encode(dummyPassword));

                        // from here changes occur ================================
            Authority a = new Authority();
            a.setName(AuthorityName.API);
            this.authorityService.save(a);

            user.getAuthorities().add(a);
            a.getUsers().add(user);
            this.repository.save(user);
            this.authorityService.save(a);
                        // end of the changes =====================================
        }

    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {


        User user = this.repository.findByUsername(username);

        UserDetails userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.getAuthorities()); // <= the final parameter (List) is now the list of Authorities

        return userDetails;
    }
}
Finally, Change the line with authenticated to hasAuthority in the SecurityConfig class
1
httpSecurity.authorizeRequests().antMatchers("/api/**").hasAuthority(AuthorityName.API.toString()).and().httpBasic(); // <= this line already existed. So change it! Don't add it!

5.6. Assignment: Add Authorities

Target

To learn how to work with the User and Authority classes

Roadmap

During this assignment you will add the entity Authority to have authorities per user

Steps
  • Given the code above

  • Implement it in your source code

Validation
  • Open postman

  • Login with username: john and password: doe

  • You should be authorised

5.7. Follow-up: Add Authorities

Below you find for this topic some extra resources to watch after the week the topic Add Authorities is introduced during the training

Youtube movies
  • movie 1

Links
  • link one

6. Summary

We have created a in memory and DB oriented Spring Security solution for securing the REST endpoints

What you should have learned
  • is that we have Spring Security for securing REST endpoints

  • is that you can use Basic Authentication for securing the REST endpoint

  • we learned how to add BCrypt

  • we learned how to use Authorities for Authorization in our Spring Boot application

What you should know
  • is that securing endpoints using Basic Authentication is only enough when you are also using SSLv3.x/TLS since otherwise the username and password Base64 encoded String can be reverted trivially.

    • The trainer might (and should) show you …​