Arvutiteaduse instituut
  1. Kursused
  2. 2024/25 kevad
  3. Ettevõttesüsteemide integreerimine (MTAT.03.229)
EN
Logi sisse

Ettevõttesüsteemide integreerimine 2024/25 kevad

  • Home
  • Lectures
  • Practicals
  • Assignements
  • Project and exam
  • Message Board

[Pre Lab] Session 9.1: Spring Boot Security - in-memory authentication and authorization

1. Clone the following repository

$ git clone https://github.com/M-Gharib/ESI-W9.1.git

2. Add the security dependency

  • Open command pallet in VSCode (Windows: Ctrl + Shift + P, Mac: Cmd + Shift + P).
  • Choose "Spring Initializer: Add starters"
  • Select "Spring security"

Or you can simply uncomment the following dependency in your Pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

3. Run your application and try to visit any of the endpoints of the application (e.g., "http://localhost:8090/api/public"), you will notice that your application will redirect you to a login page, where you are expected to enter your credentials! Anyway, there is nothing to be worried about; Spring Boot has automatically created a user and a password for you. You need just to check your application log (Terminal).


Generated password in the terminal

4. Use "user" as the username and the generated password as the password, and you will be able to access http://localhost:8090/api/public.

Why does this happen? When Spring Boot detects the spring-boot-starter-security, it will secure the access to the application by defining a single user with a randomly generated password.

Note that you can override this default user by setting up the username and password for the default user within the application.properties file, as follows:

#Defined default user credentials 
#spring.security.user.name = username 
#spring.security.user.password= password 

5. Uncomment the username and password in application.properties and run your application again. You should notice that Spring did not create a default password this time, and using the username and password you defined in application.properties should be enough to have access to http://localhost:8090/api/public. However, a single user is not enough for securing and controlling access to your application. Accordingly, comment out the username and password in application.properties because we do not need them anymore.

Creating in-memory authentication and authorization mechanism

Uncomment the code in config/SecurityConfig.java, the userDetailsService is used to create two users in-memory, assigning their user names, passwords, and roles. Moreover, we are using BCrypt Password Encoder which is a strong hashing function to encode and decode the passwords.

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

    @Bean
    //In memory user authentication and authorization 
    public UserDetailsService userDetailsService(PasswordEncoder encoder) {
        UserDetails admin = User
                .withUsername("admin")
                .password(encoder.encode("admin"))
                .roles("ADMIN")
                .build();
        UserDetails user = User
                .withUsername("user")
                .password(encoder.encode("user"))
                .roles("USER")
                .build();
    return new InMemoryUserDetailsManager(admin, user);
    }
    .....

Configuring authorization/access rules

After registering our different types of users, we can define our access rules. We will control the access to our application (routes to endpoints) considering the roles that our users play in the system. This can be done with the securityFilterChain(..) function.

    .....
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .cors(withDefaults())
                //.csrf().disable()
                .authorizeHttpRequests(authorize -> authorize
                                .requestMatchers("/api/public").permitAll()
                                .requestMatchers("/api/admin").hasRole("ADMIN")
                                .requestMatchers("/api/user").hasRole("USER")
                                .anyRequest().authenticated()
                )
                .formLogin(withDefaults())
                .httpBasic(withDefaults());
                return http.build();
    } 
    .....

The securityFilterChain(..) function takes the HttpSecurity as a parameter, and defines the following configurations:

  • .cors(withDefaults() supports the Cross-Origin Request Sharing (CORS) mechanism to avoid CORS errors since our REST API is to be accessed from a Vue.js frontend application that will run on a different port. We have already done the same for our rest controllers if you remember. However, we also need to adapt the configuration of Spring security as well.
  • .csrf().disable() we do not need it here, but we will need it later. We are disabling the protection against cross-site request forgery attacks (CSRF) since we are only using a non-browser client.
  • .authorizeHttpRequests restricts access based on RequestMatcher implementations.
  • .requestMatchers("/api/public").permitAll() ensures that any request to ("/api/public") is permitted.
  • .requestMatchers("/api/admin").hasAnyRole("ADMIN") ensures that any request to ("/api/admin ") should be authenticated and only users with Role ("ADMIN") are allowed.
  • .requestMatchers("/api/user").hasAnyRole("USER") ensures that any request to ("/api/user ") should be authenticated and only users with Role ("USER") are allowed.

Note that you can use hasRole instead of hasAnyRole if your system allows only one role for the user. Also, you may find hasAuthority is some Spring Boot applications, theoretically, it is equivalent to hasRole. Also, note that the use of .hasAnyRole("ADMIN") is not authentication but authorization as both types of users can be authenticated by only users with the role "ADMIN" who can access this route.

  • .authenticated() requires that all endpoints called be authenticated before proceeding in the filter chain.
  • .formLogin(withDefaults()) allows users to authenticate with form-based login (username/password in a form).
  • .httpBasic(withDefaults()) allows users to authenticate with HTTP Basic authentication, i.e. sending in an HTTP Basic Auth Header to authenticate.

Test our application

In RestClientFile.rest, there are three HTTP requests, the first is a public endpoint, i.e., not protected, and the other two requesters are protected and can be accessed by users who have roles of "ADMIN" and "USER" respectively. If you run the following requests in RestClient you will receive a form (an HTML page) as we configure our security (.formLogin(withDefaults())) to use a form for checking the credentials of a user. Therefore, use the browser when you want to try any of them

### Public endpoint
GET http://localhost:8090/api/public

### Protected endpoint - only admins are allowed - Username: admin Password: admin
GET http://localhost:8090/api/admin

### Protected endpoint - only users are allowed - Username: user Password: user
GET http://localhost:8090/api/user

Note When you try to log in a cookie will be created and saved in your browser, which may prevent you from trying to log in again using other credentials. In order not to wait until the cookie expires or rerun your application, you can delete this cookie manually, as follows: right-click (anywhere on the webpage), select Inspect, and navigate to the application tab. The cookie will have the name http://localhost:8090, right-click on it, then, clear. After clearing the cookie, you can try to log in again using any credentials you want.


Clearing a cookie
  • Arvutiteaduse instituut
  • Loodus- ja täppisteaduste valdkond
  • Tartu Ülikool
Tehniliste probleemide või küsimuste korral kirjuta:

Kursuse sisu ja korralduslike küsimustega pöörduge kursuse korraldajate poole.
Õppematerjalide varalised autoriõigused kuuluvad Tartu Ülikoolile. Õppematerjalide kasutamine on lubatud autoriõiguse seaduses ettenähtud teose vaba kasutamise eesmärkidel ja tingimustel. Õppematerjalide kasutamisel on kasutaja kohustatud viitama õppematerjalide autorile.
Õppematerjalide kasutamine muudel eesmärkidel on lubatud ainult Tartu Ülikooli eelneval kirjalikul nõusolekul.
Courses’i keskkonna kasutustingimused