I have some pdf files in my host machine where my backend service is deployed that I want to serve publicly. The requirement is to make file updating dynamic such that the client can replace the outdated files with new ones through a module I have created for them. Of course, I could serve the files through an endpoint but that is less efficient because it routes requests through the full application stack, adding processing overhead compared to direct static file serving by the web server.
Following is what I added in application.properties folder:
spring.web.resources.static-locations=file:/home/spidey/sopon3/rda-aof/
This works by accessing my-devdomain.com/public-data.pdf, however, due to spring security, accessing it requires authentication. I tried allowing /rda-aof/** in my security config but it doesn’t work and I cannot just allow /** because it will allow all endpoints. So what is the workaround for this? Also, feel free to suggest other methods if this isn’t going to work.
I am adding the security config code for reference:
package ca.p6son.spring.config.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.Arrays;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
Environment env;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtUserDetailsService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
/*
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
*/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
//return new BCryptPasswordEncoder(11, new SecureRandom());
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
logger.info("AuthManager init: " + super.authenticationManagerBean().toString());
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
boolean isProd = (Arrays.stream(env.getActiveProfiles()).findFirst().orElse(null)).equals("prod");
http
.authorizeRequests()
.antMatchers("/api/v1/geojson/atm",
isProd ? "/api/v1/invalid/path1" : "/swagger-ui/**",
isProd ? "/api/v1/invalid/path2" : "/v3/api-docs/**",
"/api/v1/account/resend_otp",
"/api/v1/resource/**",
"/api/v1/geojson/branches",
"/api/v1/guides",
"/api/v1/faq",
"/actuator/**",
"/api/v1/products",
"/api/v1/version_validate",
"/api/v1/security_guides",
"/api/v1/biometric_challenge", // To enable
"/api/v1/bio-override",
"/api/v1/dao/bio-override",
"/api/v1/push-notification",
"/api/v1/deals_and_offers",
"/api/v1/city_list",
"/api/v1/users",
"/api/v1/cleanup/*",
"/api/v1/account/forgot_username/*",
"/api/v1/account/forgot_password/*",
"/api/v1/auth/**",
"/api/v1/validate/pin",
"/api/v1/test",
"/api/v1/cleanup",
// "/api-docs/**",
// "/swagger-ui.html",
// "/swagger-ui/**",
// "/v3/api-docs/**",
"/inter-module/**",
"/rda-aof/**",
"/api/v1/verify-email-link",
"/api/v1/robo/**",
"/api/v1/terms_and_conditions").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.headers(headers -> {
headers.addHeaderWriter((request, response) -> {
String requestURI = request.getRequestURI();
if (!(requestURI.startsWith("/api/v1/resource/alerts") || requestURI.startsWith("/api/v1/verify-email-link"))) {
// Apply headers to all requests except the excluded endpoint
response.addHeader("Referrer-Policy", "strict-origin-when-cross-origin");
// response.addHeader("X-XSS-Protection", "1; mode=block");
response.addHeader("Content-Security-Policy", "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self'");
response.addHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
response.addHeader("Permissions-Policy", "geolocation=(self), microphone=(), camera=()");
}
});
})
.cors().and().csrf().disable(); // TODO: Security issue?
// Add a filter to validate the tokens with every request
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
2