2

Question: is my implementation secure (by API Key standards) OR as secure as using Spring Boot Security?

I have produced a Spring Boot API, but rather than using Spring Boot Security to implement Api Key security, I have written my own API key implementation. The API Key is passed as a @RequestHeader in each 'secured' request (see /booking/cancel below).

Controller:

@RequestMapping(value = "/booking/cancel",
        consumes = { "application/json" },
        method = RequestMethod.POST)
public ResponseEntity<Void> cancelOrder(@RequestBody Cancellation cancellation, 
                                       @RequestHeader String apiKey) {

        if(apiKey == null) {
            return new ResponseEntity<Void>(HttpStatus.NOT_ACCEPTABLE);
        }

        long bookingProviderId;
        try {
            bookingProviderId = bookingService.getIdFromApiKey(apiKey);
            if (bookingProviderId < 0) {
                return new ResponseEntity<Void>(HttpStatus.NOT_ACCEPTABLE);
            }
        } catch (ApplicationException e) {
            e.printStackTrace();
            return new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
        }

        //More code here...
}

Service layer:

The getIdFromApiKey function exists in my service layer and calls the Dao object. It returns a long (Id) which I can subsequently use to manage access in the controller (e.g. prevent a user from cancelling someone else's order).

public long getIdFromApiKey(String apiKey) throws ApplicationException {
    return apiKeyDao.selectId(apiKey);
}

Dao Layer:

public long getApiKey (String apiKey) throws DataAccessException {

    BookingProvider bp = jdbcTemplate.queryForObject("SELECT * FROM BookingProvider WHERE apiKey = ?", BeanPropertyRowMapper.newInstance(BookingProvider.class), apiKey);

    if(bp == null)
        return -1;
    else
        return bp.getId();
}
Harry Coder
  • 2,429
  • 2
  • 28
  • 32
Ben123
  • 335
  • 2
  • 11

1 Answers1

1

Late answer: The approach behind your code is good, but you don't need to write so much code. It is better to configure this in one place with Spring Security. It will allow to add security only on your controllers as their are the entry point of your application. See this link for more details and below is the code I am using:

application.yml

application:
  http:
    authentication:
      header-name: X-API-KEY-TEST
      api-key: X-API-KEY-VALUE-TEST

ApiKeyProperties.java

@Getter
@Setter
@NoArgsConstructor
@Configuration
@ConfigurationProperties(prefix = "application.http.authentication")
public class ApiKeyProperties {

    private String headerName;
    private String apiKey;
}

ApiKeyAuthenticationFilter.java

public class ApiKeyAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {

    private final String headerName;

    public ApiKeyAuthenticationFilter(String headerName) {
        this.headerName = headerName;
    }

    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        return request.getHeader(headerName);
    }

    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        return null;
    }
}

ApiSecurityConfiguration.java

@Configuration
@EnableWebSecurity
public class ApiSecurityConfiguration {

    private final ApiKeyProperties apiKeyProperties;

    @Autowired
    public ApiSecurityConfiguration(ApiKeyProperties apiKeyProperties) {
        this.apiKeyProperties = apiKeyProperties;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity security) throws Exception {
        var filter = new ApiKeyAuthenticationFilter(apiKeyProperties.getHeaderName());
        filter.setAuthenticationManager(authentication -> {
            var principal = (String) authentication.getPrincipal();
            if (!apiKeyProperties.getApiKey().equals(principal)) {
                throw new UnauthorizedException("The API key was not found or not the expected value");
            }
            authentication.setAuthenticated(true);
            return authentication;
        });
        security.antMatcher("/**")
                .csrf().disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .addFilter(filter)
                .authorizeRequests()
                .anyRequest()
                .authenticated();
        return security.build();
    }
}

Postman

enter image description here

Harry Coder
  • 2,429
  • 2
  • 28
  • 32