1

A Spring Boot app uses Spring Security to protect elements of its API from unauthorized users. But the app has public url patterns that need to be callable from un-authenticated users, as long as those users are from certain apps. What specific changes need to be made to the code below so that the /some-test-service end point can be successfully called from an un-authenticated user from a specific calling location?

I will know, for example, that the terminal commands are being run from the same localhost that runs the Spring Boot app. I will also know that the front end AngularJS/Node.js calling app is running on a specific port, like port 9000.

Here is what happens when I type in the following POST test from the CentOS terminal in the devbox that runs the Spring Boot app:

[user@localhost controllers]$ curl -i -X POST -H "Content-Type:application/json" -d '{  "wleadid" : "1" }' http://localhost:8001/some-test-service
HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=530A75F962CCFB95EEDF43051BC71573; Path=/; HttpOnly
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 06 Apr 2016 09:11:09 GMT
{"timestamp":1459933869769,"status":403,"error":"Forbidden","message":"Expected CSRF token not found. Has your session expired?","path":"/some-test-service"}  
[user@localhost controllers]$ 

And here is the Spring Security config, in the complete UiApplication.java that that is the main class of the Spring Boot app:

@SpringBootApplication
@Controller
@EnableJpaRepositories(basePackages = "demo", considerNestedRepositories = true)
public class UiApplication extends WebMvcConfigurerAdapter {

    @Autowired
    private Users users;

    public static void main(String[] args) {
        SpringApplication.run(UiApplication.class, args);
    }

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

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver slr = new SessionLocaleResolver();
        slr.setDefaultLocale(Locale.US);
        return slr;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        lci.setParamName("lang");
        return lci;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }

    @Order(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    protected static class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {

        @Autowired
        private Users users;

        @Override
        public void init(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(users);
        }
    }

    @SuppressWarnings("deprecation")
    @Configuration
    @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
    @EnableWebMvcSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .formLogin()
                    .successHandler(new MyAuthenticationSuccessHandler())
                    .and()
                .httpBasic().and()
                .authorizeRequests()
                    .antMatchers("/some-test-service").permitAll()
                    .antMatchers("/", "/url1", "/url2", "/url3*", "/url4*", "/url5*").permitAll()
                    .anyRequest().authenticated()
                    .and()
                .csrf()
                    .csrfTokenRepository(csrfTokenRepository())
                    .and()
                .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
        }

        private Filter csrfHeaderFilter() {
            return new OncePerRequestFilter() {
                @Override
                protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                        throws ServletException, IOException {
                    System.out.println("----- INSIDE new OncePerRequestFilter().doFilterInternal(...) ");
                    CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
                    if (csrf != null) {
                        Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
                        String token = csrf.getToken();
                        if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                            cookie = new Cookie("XSRF-TOKEN", token);
                            cookie.setPath("/");
                            response.addCookie(cookie);
                        }
                    }
                    filterChain.doFilter(request, response);
                    System.out.println("csrf.getToken() is: "+csrf.getToken());
                }
            };
        }

        private CsrfTokenRepository csrfTokenRepository() {
            HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
            repository.setHeaderName("X-XSRF-TOKEN");
            System.out.println("repository.toString() is: "+repository.toString());
            return repository;
        }

    }

    @Repository
    interface UserRepository extends CrudRepository<User, Long> {
        User findByName(String name);
    }

}
CodeMed
  • 9,527
  • 70
  • 212
  • 364
  • You can follow this post http://stackoverflow.com/questions/30366405/how-to-disable-spring-security-for-particular-url – Tran Ho Apr 07 '16 at 00:41
  • @kimdung Thank you. Please leave your comment so that others can read it also. But please note that MDienum's answer in the other post uses a `WebSecurity` object in `SecurityConfig`, while the version in this OP uses an `HttpSecurity`. Also, the error message in the present OP specifies `XSRF certificate` problems, while the error in the link you gave did not. I hesitate to change the input to `SecurityConfig` without knowing more about it, and also I suspect that the `XSRF` issue is important. This URL is callable using the present config when the AngularJS app is inside the same JAR. – CodeMed Apr 07 '16 at 00:52

1 Answers1

0

I had a similar problem. But my configuration works without csrf. Link with public access: "localhost:8080/someTestUrl/", but other references required authorization.

My congfiguration:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
MongoDBAuthenticationProviderService authenticationProvider;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProvider);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();

    http
            .anonymous()
            .and()
            .authorizeRequests().antMatchers("/someTestUrl").permitAll()
            .and()
            .formLogin().permitAll().loginPage("/login").usernameParameter("username").passwordParameter("password")
            .and()
            .logout().permitAll()
            .and()
            .authorizeRequests().anyRequest().fullyAuthenticated()
            .and()
            .httpBasic().disable()
            .exceptionHandling().accessDeniedPage("/403")
            .and()
            .headers()
            .contentTypeOptions()
            .disable();
}
}

And ResourseConfig:

@EnableResourceServer
@Configuration
public class ResourseConfig extends ResourceServerConfigurerAdapter {

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

    http
            .anonymous()   
            .and()
            .authorizeRequests().antMatchers("/someTestUrl/**").permitAll()
            .and()
            .formLogin().permitAll().loginPage("/login").usernameParameter("username").passwordParameter("password")
            .and()
            .logout().permitAll()
            .and()
            .authorizeRequests().anyRequest().fullyAuthenticated() 
            .and()
            .httpBasic().disable()
            .exceptionHandling().accessDeniedPage("/403")
            .and()
            .headers().contentTypeOptions()
            .disable();
}
}

Maybe it will help you.

Swyd
  • 41
  • 8
  • Adding `.anonymous().and().authorizeRequests().antMatchers("/someTestUrl").permitAll().and()` before the `formLogin()...` part of `SecurityConfig` did not resolve the problem. What is `ResourceConfig`? – CodeMed Apr 07 '16 at 19:11
  • @CodeMed did you ever resolve the issue? – Ka_Papa Mar 08 '23 at 19:48