HTTP 403 - 为什么呢? Spring Security + REST

时间:2014-06-01 22:31:07

标签: spring rest spring-mvc authentication spring-security

我正在开发Spring MVC应用程序,我对发生的情况感到有些困惑。

我提供了2个入口点:

  • 标准表单登录,就像魅力一样。

第二个是REST入口点。

我的配置如下:

@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
    }
}
}  

我的控制器:

@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;
    }
}
}

我正在使用Spring 4.0.0.RELEASE,Spring Security 3.2.4.RELEASE。

cUrl请求:

curl -i -GET -u my.user:password -H 'Accept: application/json'  http://localhost:8080/api/meetings/9

在回复中,我获得了HTTP 403代码的默认html页面内容。

HTTP 403表示用户已通过身份验证,但没有必要的权限来请求内容。

但是,我的用户拥有权限USER,使他有权获取内容。

我希望使用JSON内容获得200 - OK状态。

我的配置中遗漏了什么?

我在某个地方犯了错误吗?

有人可以澄清如何使用安全性成功创建RESTful服务吗?

SOLUTION:

好的,基本上我忘记了正确设置REST服务的几件事:

  1. 提供AuthenticationEntryPoint,以便在登录时发送错误。
  2. 启用容器(在我的情况下为Tomcat)以支持HTTPS
  3. @RequestMapping注释中的正确MediaType(MediaType.APPLICATION_JSON_VALUE)。
  4. 以下工作代码:

    @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 {
    
        }
    }
    

    }

    控制器:

    @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;
        }
    }
    }
    

    希望我帮助过任何人,

    干杯

0 个答案:

没有答案