我正在开发基于Spring Boot(spring-boot-starter-web)的REST API,其中使用Spring Security(spring-security-coree spring-security-config)保护不同的端点。
spring-boot-starter-web
spring-security-core
spring-security-config
身份验证是通过使用本地数据库完成的,该本地数据库包含具有两个不同角色集的用户:ADMIN和USER。USER应该能够访问GET所有API端点以及POST基于的端点routeA。ADMIN应该能够与基于routeB的USERplus POST和DELETEend相同
ADMIN
USER
GET
POST
routeA
DELETE
但是,我得到的行为是,我可以GET向任何端点发出请求,但是对于任何一种类型的用户,POST请求总是返回HTTP 403 Forbidden- ADMIN和USER-这不是我期望的基于我的SecurityConfiguration。
HTTP 403 Forbidden
SecurityConfiguration
关于我缺少什么的任何想法?
SecurityConfiguration.java
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class); @Autowired private RESTAuthenticationEntryPoint authenticationEntryPoint; @Autowired private DataSource dataSource; @Override public void configure(AuthenticationManagerBuilder builder) throws Exception { logger.info("Using database as the authentication provider."); builder.jdbcAuthentication().dataSource(dataSource).passwordEncoder(new BCryptPasswordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and(). authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER") .antMatchers(HttpMethod.POST, "/routeA/*").hasAnyRole("ADMIN", "USER") .antMatchers(HttpMethod.POST, "/routeB/*").hasRole("ADMIN") .antMatchers(HttpMethod.DELETE, "/routeB/*").hasRole("ADMIN").and(). requestCache().requestCache(new NullRequestCache()).and(). httpBasic().authenticationEntryPoint(authenticationEntryPoint).and(). cors(); } @Bean public CorsConfigurationSource corsConfigurationSource() { final CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("*")); configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH")); configuration.setAllowCredentials(true); configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type")); final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; }
RouteBController .java
@RestController public class RouteBController { static final Logger logger = LoggerFactory.getLogger(RouteBController.class); public RouteBController() { } @RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET) public String getStuff() { return "Got a hello world!"; } @RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST) public String postStuff() { return "Posted a hello world!"; } }
RESTAuthenticationEntryPoint.java
@Component public class RESTAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { @Override public void afterPropertiesSet() throws Exception { setRealmName("AppNameHere"); super.afterPropertiesSet(); } }
有2个问题SecurityConfiguration.java使其无法正常运行。
尽管该403 Forbidden错误消息未包含任何指示其失败原因的消息(请参见下面的示例),但事实证明,这是由于启用了CSRF而导致的。禁用它允许POST和DELETE要求进行处理。
403 Forbidden
{ "timestamp": "2018-06-26T09:17:19.672+0000", "status": 403, "error": "Forbidden", "message": "Forbidden", "path": "/routeB" }
另外,antMatched(HttpMethod, String)for RouteB中使用的表达式也是错误的,因为/routeB/*期望它在after之后有 一些东西/。正确的配置是/routeB/**因为 可以 存在( 或不 存在)更多路径。
antMatched(HttpMethod, String)
RouteB
/routeB/*
/
/routeB/**
该 修正 SecurityConfiguration.java IS
@Override protected void configure(HttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and(). authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER") .antMatchers(HttpMethod.POST, "/routeA/**").hasAnyRole("ADMIN", "USER") .antMatchers(HttpMethod.POST, "/routeB/**").hasRole("ADMIN") .antMatchers(HttpMethod.DELETE, "/routeB/**").hasRole("ADMIN").and(). requestCache().requestCache(new NullRequestCache()).and(). httpBasic().authenticationEntryPoint(authenticationEntryPoint).and(). cors().and(). csrf().disable(); }