In this post, you will see code samples for how to add basic authentication to Spring Boot API. The code samples in this post will show how to secure certain endpoints with Spring security such that they can be only accessed via the correct username and password. This post will also include links to the Github repository that has the full source code for this API along with the Github action to build it every time you commit the code.

The sample API

The API you will create will be a simple REST API with the following endpoints,

# Hello endpoints
/say/hello
/say/helloNoAuth
/say/hello/service

# User endpoints
/api/user/ping
/api/user/create
/api/user/test2

Your goal is to secure all of the above API endpoints except for /say/helloNoAuth and /api/user/ping.

How to add basic authentication to Spring Boot API

You can start by adding controllers,

@RestController
@RequestMapping("/say")
public class HelloController {
    @Autowired
    private HelloServiceImpl helloService;

    @GetMapping(value = "/hello")
    public String sayHello() {
        return "Hello World";
    }
    @GetMapping(value = "/helloNoAuth")
    public String helloNoAuth() {
        return "Hello World with no auth";
    }
    @GetMapping(value = "/hello/service")
    public ResponseEntity<String> helloFromService() {
        return new ResponseEntity<>(helloService.greeting(), HttpStatus.OK);
    }
}

p.s. If you copy and paste this code in your IDE and notice compile time errors, you can auto-resolve most of them. In case you are not sure about the packages the imports are from you can checkout the full source code in the Github repository link in the conclusion section.

The user controller

@RestController
@RequestMapping("/api/user")
@Slf4j
public class UserController {

    @GetMapping(value = "/ping")
    private ResponseEntity<CustomUser> pingUser() {
        List<GrantedAuthority> ROLE_USER = Collections
                .unmodifiableList(AuthorityUtils.createAuthorityList("ROLE_USER"));
        CustomUser cUser = new CustomUser("bsoni", "1234", ROLE_USER);
        return new ResponseEntity<>(cUser, HttpStatus.OK);
    }
    @PostMapping("/create")
    public ResponseEntity<Object> createUser(@RequestBody CreateUserRequest createUserRequest) {
        String ROLE_PREFIX = "ROLE_";
        log.info(createUserRequest.toString());
        log.info("About to create user");
        if(ObjectUtils.isEmpty(createUserRequest)) {
            return new ResponseEntity<>("Please supply create user body", HttpStatus.BAD_REQUEST);
        }
        List<GrantedAuthority> auths = new ArrayList<>();
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        for(String role: createUserRequest.getRoles()) {
            auths.add(new SimpleGrantedAuthority(ROLE_PREFIX + role.toUpperCase()));
        }

        CustomUser cUser = new CustomUser(createUserRequest.getUsername(), bCryptPasswordEncoder.encode(createUserRequest.getPassword()), auths);
        log.info(cUser.toString());
        InMemoryUserDetailsManager inMemoryUserDetailsManager = CustomUserDetailsManager.instance.getInMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(cUser);
        log.info("Successfully created the user");
        return new ResponseEntity<>(cUser, HttpStatus.CREATED);
    }
    @GetMapping("/test2")
    public ResponseEntity<String> test2() {
        return new ResponseEntity<>("Doing another test", HttpStatus.OK);
    }
}

As you can see above there is a lot happening in the UserController, mostly because to demonstrate certain concepts for this post, the code hasn’t been segregated into a Service layer. In order to create a user, with the above code, you are just re-using the InMemoryUserDetailsManager class from Spring Security. As the name implies, the class maintains a list of users in memory when the application runs. This means the users need to be added to the in memory user store every time the application starts, for the users to be able to login. Next, you will write one of the most important aspects of this API.

Security Configuration (or security config)

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // setup basic authentication
        http.httpBasic(withDefaults())
                    .authorizeHttpRequests(auth -> {
                        try {
                            auth
                                    .requestMatchers("/api/user/create").hasAnyRole("USER")
                                    .requestMatchers("/index"
                                            ,"/favicon.ico"
                                            ,"/api/user/ping"
                                            ,"/say/helloNoAuth")
                                    .permitAll()
                                    .anyRequest()
                                    .authenticated();
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    });
        // configure the csrf token to ignore the post request to create new users
        http.csrf(httpSecurityCsrfConfigurer ->
                httpSecurityCsrfConfigurer.ignoringRequestMatchers("/api/user/create"));
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = CustomUserDetailsManager.instance.getInMemoryUserDetailsManager();
        UserDetails userDetails = User.withUsername("bsoni")
                .password(passwordEncoder().encode("password"))
                .roles("ADMIN", "USER")
                .build();
        inMemoryUserDetailsManager.createUser(userDetails);;
        return inMemoryUserDetailsManager;
    }
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

Remember your goal was to secure all the API endpoints, except for /say/helloNoAuth and /api/user/ping. You are doing exactly that from line 10 to line 18.

All the logic does is, allow the endpoints without any authentication whereas, require the user to be authenticated for any other API calls.

Conclusion

If you want to see a full working project for the code shared in this blog post, you can access it all on the Github repo custom-spring-boot-basic-auth.

If you have any questions on this, feel free to leave a comment on this post or send me an email directly.

If you find any of my posts useful and want to support me, you can buy me a coffee 🙂

https://www.buymeacoffee.com/bhumansoni

While you are here, maybe try one of my apps for the iPhone.

Products – My Day To-Do (mydaytodo.com)

Here are some of my other bloposts on Java

How to build a full stack Spring boot API with ReactJS frontend – My Day To-Do (mydaytodo.com)

How to call REST API with WebClient – My Day To-Do (mydaytodo.com)

How to build a jokes client in Java Spring Boot with RestTemplate – My Day To-Do (mydaytodo.com)

Have a read of some of my other posts on AWS

Upload to AWS S3 bucket from Java Spring Boot app – My Day To-Do (mydaytodo.com)

Deploy NodeJS, Typescript app on AWS Elastic beanstalk – (mydaytodo.com)

How to deploy spring boot app to AWS & serve via https – My Day To-Do (mydaytodo.com)


0 Comments

Leave a Reply

Avatar placeholder
Verified by MonsterInsights