我正在使用JAX-RS和Spring Boot开发REST API应用程序。我的所有资源都由JAX-RS提供。安全性由Spring安全性处理。登录和注销端点是默认的(我的api中没有定义任何登录或注销端点,我也没有接受OPTIONS定义的任何方法,正如我在一些类似的问题中看到的那样)。问题是当我尝试从网页登录(使用JavaScript)时,浏览器首先发送预检OPTIONS请求以检查功能。不幸的是,它由NotFoundExceptionMapper类提供404服务。
浏览器部分:
'(^|.*[^a-zA-Z0-9_])[.@.][[:<:]]test123[[:>:]]'
并在控制台中:
OPTIONS http://localhost:8080/xxx/login
(index):1 XMLHttpRequest cannot load http://localhost:8080/xxx/login. Response for preflight has invalid HTTP status code 404
当我使用例如Postman发布登录请求然后发送任何我的端点的OPTIONS请求时,我得到了正确的响应(这由JAX-RS处理)。
来自我的父pom.xml
o.s.security.web.FilterChainProxy : /login at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
o.s.security.web.FilterChainProxy : /login at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
o.s.security.web.FilterChainProxy : /login at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
o.s.security.web.FilterChainProxy : /login at position 4 of 12 in additional filter chain; firing Filter: 'LogoutFilter'
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/logout'
o.s.security.web.FilterChainProxy : /login at position 5 of 12 in additional filter chain; firing Filter: 'CorsFilter'
o.s.security.web.FilterChainProxy : /login at position 6 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /login' doesn't match 'POST /login
o.s.security.web.FilterChainProxy : /login at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
o.s.security.web.FilterChainProxy : /login at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
o.s.security.web.FilterChainProxy : /login at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
o.s.security.web.FilterChainProxy : /login at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
o.s.security.web.FilterChainProxy : /login at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
o.s.security.web.FilterChainProxy : /login at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
o.s.s.w.u.matcher.AntPathRequestMatcher : Request '/login' matched by universal pattern '/**'
o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /login; Attributes: [permitAll]
o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@5cc50bdc, returned: 1
o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
o.s.security.web.FilterChainProxy : /login reached end of additional filter chain; proceeding with original chain
o.s.web.filter.RequestContextFilter : Bound request context to thread: SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper@7ebf4076]
o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@4dc961c8
w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
o.s.web.filter.RequestContextFilter : Cleared thread-bound request context: SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper@7ebf4076]
o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
WebSecurityConfig.java
<spring.version>4.3.1.RELEASE</spring.version>
<spring.security.version>4.1.1.RELEASE</spring.security.version>
<spring.boot.version>1.3.6.RELEASE</spring.boot.version>
....
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
我的休息处理人员没有做任何特别的事情。他们只返回JSON响应而不是重定向。
CorsFilter.java
@EnableWebSecurity
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String NON_RESTRICTED_OPTS_URL = "/**";
//autowired handlers, userdetails and some static string arrays holding urls
protected void configure(HttpSecurity http) throws Exception {
configureAuthentication(http);
configureResourcesAccess(http);
configureFilters(http);
configureSession(http);
};
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(
new BCryptPasswordEncoder());
}
private void configureAuthentication(HttpSecurity http) throws Exception {
http.formLogin().successHandler(restAuthSuccessHandler)
.failureHandler(restAuthFailureHandler).and().logout()
.logoutSuccessHandler(restLogoutSuccessHandler).and()
.exceptionHandling()
.authenticationEntryPoint(restAuthEntryPoint)
.accessDeniedHandler(restAccessDeniedHandler);
}
private void configureFilters(HttpSecurity http) throws Exception {
http.csrf()
.disable() // temporarily disable csrf
.addFilterBefore(new CorsFilter(),
UsernamePasswordAuthenticationFilter.class);
}
private void configureResourcesAccess(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, NON_RESTRICTED_OPTS_URL)
.permitAll()
.antMatchers(HttpMethod.POST, NON_RESTRICTED_POST_URLS)
.permitAll()
.antMatchers(HttpMethod.PUT, NON_RESTRICTED_PUT_URLS)
.permitAll().anyRequest().fullyAuthenticated();
}
private void configureSession(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(
SessionCreationPolicy.IF_REQUIRED);
}
}
JerseyConfig.java
public class CorsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Origin",
request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods",
"POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers",
"Content-Type, Accept, X-Requested-With, remember-me");
chain.doFilter(request, response);
}
}
我尝试通过Spring的@RequestMapping添加一个/ login方法,如某些网页上所建议的那样,添加了一个接受所有url和OPTIONS方法的方法(默认情况下我没有调度OPTIONS请求)但仍然没有运气。< / p>