2.1 Starting with the first project

  1. the dependencies you need to write

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-web</artifactId>
    4. </dependency>
    5. <dependency>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-security</artifactId>
    8. </dependency>
  2. create Hello-Controller

    1. @RestController
    2. public class HelloController {
    3. @GetMapping("/hello")
    4. public String hello() {
    5. return "Hello!";
    6. }
    7. }
  3. run the application, besides the other lines in the console, you should see:

image.png

  1. call the endpoint
  • without using Authorization header:

image.png

  • using Authorization header:

image.png

2.2 Which are the default configurations?

image.png
In the following text, will discuss these autoconfigured beans:

  • UserDetailsService
  • PasswordEncoder

The authentication provider uses these beans to find users and to check their passwords.

2.3 Overriding default configurations

  1. The configuration class for the UserDetailsService bean

    1. @Configuration
    2. public class ProjectConfig {
    3. @Bean
    4. public UserDetailsService userDetailsService() {
    5. var userDetailsService = new InMemoryUserDetailsManager();
    6. return userDetailsService;
    7. }
    8. }
  2. Creating a user with the User builder class for UserDetailsService ```java @Configuration public class ProjectConfig{

    @Override @Bean public UserDetailsService userDetailsService() {

    1. var userDetailsService = new InMemoryUserDetailsManager();
    2. var user = User.withUsername("john")
    3. .password("12345")
    4. .authorities("read")
    5. .build();
    6. userDetailsService.createUser(user);
    7. return userDetailsService;

    }

}

  1. 3. add a PasswordEncoder bean in the context
  2. ```java
  3. @Bean
  4. public PasswordEncoder passwordEncoder() {
  5. return NoOpPasswordEncoder.getInstance();
  6. }
  1. call the endpoint

image.png\

  1. Extending WebSecurityConfigurerAdapter ```java @Configuration public class ProjectConfig extends WebSecurityConfigurerAdapter { …

    @Override protected void configure(HttpSecurity http) throws Exception {

    1. //...

    }

}

  1. 6. Using the HttpSecurity parameter to alter the configuration
  2. ```java
  3. @Configuration
  4. public class ProjectConfig extends WebSecurityConfigurerAdapter {
  5. //...
  6. @Override
  7. protected void configure(HttpSecurity http) throws Exception {
  8. http.httpBasic();
  9. http.authorizeRequests().anyRequest().authenticated();
  10. }
  11. }
  1. The code configures endpoint authorization with the same behavior as the default one. You can call the endpoint again to see that it behaves the same as in the previous one. With a slight change, you can make all the endpoints accessible without the need for credentials.
  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. http.httpBasic();
  4. http.authorizeRequests().anyRequest().permitAll();
  5. }

2.3.3 Setting the configuration in different ways

  • Setting UserDetailsService and PasswordEncoder in configure()

    1. @Configuration
    2. public class ProjectConfig extends WebSecurityConfigurerAdapter {
    3. @Override
    4. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    5. var userDetailsService = new InMemoryUserDetailsManager();
    6. var user = User.withUsername("john")
    7. .password("12345")
    8. .authorities("read")
    9. .build();
    10. userDetailsService.createUser(user);
    11. auth.userDetailsService(userDetailsService)
    12. .passwordEncoder(NoOpPasswordEncoder.getInstance());
    13. }
    14. }

  • Configuring in-memory user management
    1. @Override
    2. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    3. auth.inMemoryAuthentication()
    4. .withUser("john")
    5. .password("12345")
    6. .authorities("read")
    7. .and()
    8. .passwordEncoder(NoOpPasswordEncoder.getInstance());
    9. }
    this approach is not recommend.

    2.3.4 Overrding the AuthenticationProvider implementation

    It’s time to learn that you can also customize the component that delegates to these, the AuthenticationProvider
    image.png
  1. Implementing the AuthenticationProvider interface

    1. @Component
    2. public class CustomAuthenticationProvider implements AuthenticationProvider {
    3. @Override
    4. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    5. //...
    6. }
    7. @Override
    8. public boolean supports(Class<?> authenticationType) {
    9. //...
    10. }
    11. }
  2. Implementing the authenticating logic

    1. @Override
    2. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    3. String username = authentication.getName();
    4. String password = String.valueOf(authentication.getCredentials());
    5. if ("john".equals(username) && "12345".equals(password)) {
    6. return new UsernamePasswordAuthenticationToken(username, password, Arrays.asList());
    7. } else {
    8. throw new AuthenticationCredentialsNotFoundException("Error!");
    9. }
    10. }
  3. The full implementation of the authentication provider ```java @Component public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException {

    1. String username = authentication.getName();
    2. String password = String.valueOf(authentication.getCredentials());
    3. if ("john".equals(username) && "12345".equals(password)) {
    4. return new UsernamePasswordAuthenticationToken(username, password, Arrays.asList());
    5. } else {
    6. throw new AuthenticationCredentialsNotFoundException("Error!");
    7. }

    }

    @Override public boolean supports(Class<?> authenticationType) {

    1. return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authenticationType);

    } }

  1. 4. Registering the new implementation of AuthenticationProvider
  2. ```java
  3. @Configuration
  4. public class ProjectConfig extends WebSecurityConfigurerAdapter {
  5. @Autowired
  6. private CustomAuthenticationProvider authenticationProvider;
  7. @Override
  8. protected void configure(AuthenticationManagerBuilder auth) {
  9. auth.authenticationProvider(authenticationProvider);
  10. }
  11. @Override
  12. protected void configure(HttpSecurity http) throws Exception {
  13. http.httpBasic();
  14. http.authorizeRequests()
  15. .anyRequest().authenticated();
  16. }
  17. }

2.3.5 Using multiple configuration classes in your project

  1. Defining the configuration class for user and password management

    1. @Configuration
    2. public class UserManagementConfig {
    3. @Bean
    4. public UserDetailsService userDetailsService() {
    5. var userDetailsService = new InMemoryUserDetailsManager();
    6. var user = User.withUsername("john")
    7. .password("12345")
    8. .authorities("read")
    9. .build();
    10. userDetailsService.createUser(user);
    11. return userDetailsService;
    12. }
    13. @Bean
    14. public PasswordEncoder passwordEncoder() {
    15. return NoOpPasswordEncoder.getInstance();
    16. }
    17. }
  2. Defining the configuration class for authorization management

    1. @Configuration
    2. public class WebAuthorizationConfig extends WebSecurityConfigurerAdapter {
    3. @Override
    4. protected void configure(HttpSecurity http) throws Exception {
    5. http.httpBasic();
    6. http.authorizeRequests().anyRequest().authenticated();
    7. }
    8. }