title: HTTP 403 - why actually? Spring Security + REST
body: <p>I am developing Spring MVC app and I am slightly confused about the situation that takes place.</p>
  
  <p>I provided 2 entry points:</p>
  
  <ul>
  <li>Standard form login which works like a charm.</li>
  </ul>
  
  <p>The second one is the REST entry point.</p>
  
  <p>My configuration below:</p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  @ComponentScan("com.springapp.mvc")
  @Order(1)
  public class ApiWebConfigurationAdapter extends WebSecurityConfigurerAdapter {
  
  @Autowired
  private ApplicationAuthenticationProvider authenticationProvider;
  
  @Override
  protected void configure(HttpSecurity http) throws Exception {        
      http
              .httpBasic()
          .and()
              .antMatcher("/api/**")
              .authorizeRequests()
              .anyRequest().hasRole(Role.USER);
      http
              .sessionManagement()
              .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
      /*http
              .requiresChannel()
              .antMatchers("/api*//**")
              .requiresSecure();*/
  
  }
  
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.authenticationProvider(authenticationProvider)
          .authenticationEventPublisher(authenticationEventPublisher())
          .eraseCredentials(true);
  }
  
  AuthenticationEventPublisher authenticationEventPublisher() {
      return new DefaultAuthenticationEventPublisher(new ApiWebApplicationEventPublisher());
  }
  
  static final class ApiWebApplicationEventPublisher implements org.springframework.context.ApplicationEventPublisher {
  
      @Override
      public void publishEvent(ApplicationEvent event) {
          event.getSource(); //just to check while debugging
      }
  }
  }  
  </code></pre>
  
  <p>My Controller:</p>
  
  <pre><code>@Controller
  @RequestMapping(value = "/api",
              produces = MediaType.APPLICATION_JSON_VALUE)
  class APIController {
  
  @Autowired
  private MeetingsService meetingsService;
  
  @RequestMapping(value="/meetings/{id}",
          method= RequestMethod.GET,
          headers = {"Content-type=application/json"})
  @ResponseStatus(HttpStatus.OK)
  @ResponseBody
  public Meeting returnMeetingByIdOrEmpty(@PathVariable("id") final Long id) {
      try {
          final Meeting meeting = meetingsService.findById(id).get();
          return meeting;
      } catch (RuntimeException e) {
          return null;
      }
  }
  }
  </code></pre>
  
  <p>I am using Spring 4.0.0.RELEASE, Spring Security 3.2.4.RELEASE.</p>
  
  <p>cUrl request:</p>
  
  <pre><code>curl -i -GET -u my.user:password -H 'Accept: application/json'  http://localhost:8080/api/meetings/9
  </code></pre>
  
  <p>In reponse I am given default html page content with HTTP 403 code. </p>
  
  <p>HTTP 403 means that user is authenticated but does not have required authorities to request the content. </p>
  
  <p>However, my user has Authority USER which entitles him to get the content.</p>
  
  <p>I am expecting 200 - OK status with JSON content.</p>
  
  <p>Am I missing something in my configuration?</p>
  
  <p>Am I making mistake somewhere?</p>
  
  <p>Could someone clarify how to successfully create RESTful service with Security?</p>
  
  <p><strong>SOLUTION:</strong></p>
  
  <p>OK, so basically I forgot about several things to set up REST service properly:</p>
  
  <ol>
  <li>Providing AuthenticationEntryPoint sending HTTP 401 in case of login with error.</li>
  <li>enabling container (Tomcat in my case) to support HTTPS</li>
  <li>Proper MediaType (MediaType.APPLICATION_JSON_VALUE) in @RequestMapping annotation.</li>
  </ol>
  
  <p>Working code below:</p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  @ComponentScan("com.springapp.mvc")
  @Order(1)
  public class ApiWebConfigurationAdapter extends WebSecurityConfigurerAdapter {
  
  @Autowired
  private ApplicationAuthenticationProvider authenticationProvider;
  
  @Override
  protected void configure(HttpSecurity http) throws Exception {
      http
              .httpBasic()
              .authenticationEntryPoint(apiEntryPoint())
          .and()
              .formLogin()
              .failureHandler(failureHandler())
              .successHandler(successHandler())
          .and()
              .antMatcher("/api/**")
              .authorizeRequests()
              .anyRequest()
              .fullyAuthenticated();
      http
              .sessionManagement()
              .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
      http
              .requiresChannel()
              .antMatchers("/api/**")
              .requiresSecure();
      http
              .portMapper()
              .http(8080)
              .mapsTo(8443);
  }
  
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.authenticationProvider(authenticationProvider)
          .authenticationEventPublisher(authenticationEventPublisher())
          .eraseCredentials(true);
  }
  
  AuthenticationEventPublisher authenticationEventPublisher() {
      return new DefaultAuthenticationEventPublisher(new ApiWebApplicationEventPublisher());
  }
  
  AuthenticationEntryPoint apiEntryPoint() {
      return new ApiEntryPoint();
  }
  
  AuthenticationFailureHandler failureHandler() {
      return new ApiAuthenticationFailureHandler();
  }
  
  AuthenticationSuccessHandler successHandler() {
      return new ApiAuthenticationSuccessHandler();
  }
  
  static final class ApiWebApplicationEventPublisher implements org.springframework.context.ApplicationEventPublisher {
  
      @Override
      public void publishEvent(ApplicationEvent event) {
  
      }
  }
  
  static final class ApiEntryPoint implements AuthenticationEntryPoint {
  
      @Override
      public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
          response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
      }
  }
  
  static final class ApiAuthenticationFailureHandler implements AuthenticationFailureHandler {
  
      @Override
      public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
  
      }
  }
  
  static final class ApiAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
  
      @Override
      public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
  
      }
  }
  </code></pre>
  
  <p>}</p>
  
  <p>Controller:</p>
  
  <pre><code>@Controller
  @RequestMapping(value = "/api")
  class APIController {
  
  @Autowired
  private MeetingsService meetingsService;
  
  @RequestMapping(value="/meetings/{id}.json", method= RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
  @ResponseStatus(HttpStatus.OK)
  @ResponseBody
  public Object returnMeetingByIdOrEmpty(@PathVariable("id") final Long id) {
      try {
          final Meeting meeting = meetingsService.findById(id).get();
          return meeting;
      } catch (RuntimeException e) {
          return JSONObject.NULL;
      }
  }
  }
  </code></pre>
  
  <p>Hope I helped anyone,</p>
  
  <p>Cheers</p>