此问题已在此处有答案:
CORS issue - No 'Access-Control-Allow-Origin' header is present on the requested resource(11个回答)
8小时前关闭
CORS策略已阻止从源“http://localhost:3000”访问位于“https://b304-210-121-223-90.ngrok.io/members/sign-up”的XMLHttpRequest:对印前检查请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。
我正在研究 Spring Boot 和React。
当前端在浏览器上发送请求时,我会遇到这个问题。不过,在 Postman 里也能用。
我在ngork只得到200个选项。
这是我的密码
安全配置
@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig
{
private final JwtService jwtService;
private final RedisRepository redisRepository;
private final CustomAuthorityUtils authorityUtils;
private final MemberDetailsService memberDetailsService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception
{
http
.headers((headers) ->
headers.frameOptions((frameOptions) -> frameOptions.disable()))
.csrf(csrf -> csrf.disable())
.httpBasic(httpBasic -> httpBasic.disable())
.formLogin(formLogin -> formLogin.disable())
.sessionManagement(sessionManagement ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling(exceptionHandling ->
exceptionHandling
.accessDeniedHandler(new CustomAccessDeniedHandler())
.authenticationEntryPoint(new CustomAuthenticationEntryPoint()))
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
.requestMatchers(HttpMethod.GET).permitAll()
.requestMatchers(HttpMethod.PATCH, "/members/help/reset-password").permitAll()
.requestMatchers(HttpMethod.POST, "/members/login", "/members/sign-up").permitAll()
.requestMatchers(HttpMethod.POST).hasAnyRole("USER", "ADMIN")
.requestMatchers(HttpMethod.PATCH).hasAnyRole("USER", "ADMIN")
.requestMatchers(HttpMethod.DELETE).hasAnyRole("USER", "ADMIN")
.anyRequest().permitAll())
.apply(new CustomFilterConfigurer());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder()
{
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity>
{
@Override
public void configure(HttpSecurity builder) throws Exception
{
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager, jwtService, redisRepository);
jwtAuthenticationFilter.setFilterProcessesUrl("/members/login");
jwtAuthenticationFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessfulHandler());
jwtAuthenticationFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtService, authorityUtils, memberDetailsService);
builder
.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
}
}
}
在CustomFilerConfigurer中,我添加了扩展UsernamePasswordAuthenticationFilter和OncePerRequestFilter的Filter类
VerificationFilter
@Slf4j
@RequiredArgsConstructor
public class JwtVerificationFilter extends OncePerRequestFilter
{
private final JwtService jwtService;
private final CustomAuthorityUtils authorityUtils;
private final MemberDetailsService memberDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
{
log.info("===doFilterInternal===");
try {
String accessToken = resolveAccessToken(request);
jwtService.isValidToken(accessToken);
setAuthenticationToContext(jwtService.getClaims(accessToken));
log.info("accessToken = {}", accessToken);
} catch (InvalidTokenException e){
log.error("Error processing JWT: {}", e.getMessage());
throw e;
}
filterChain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException
{
String authorization = request.getHeader(AUTHORIZATION);
log.info("===shouldNotFilter===");
log.info("authorization = {}", authorization);
return authorization == null || !authorization.startsWith("Bearer ");
}
private void setAuthenticationToContext(Claims claims)
{
String email = claims.getSubject();
UserDetails userDetails = memberDetailsService.loadUserByUsername(email);
List<GrantedAuthority> authorities = authorityUtils.createAuthorities((List) claims.get("roles"));
log.info("===setAuthenticationToContext===");
log.info("authorities = {}", authorities);
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
private String resolveAccessToken(HttpServletRequest request)
{
return request.getHeader(AUTHORIZATION).substring(7);
}
}
在shouldNotFilter中,如果请求在HttpHeader中不包含jwt token或Authorization,则返回true。使得
doFilterInternal不会为请求执行。
身份验证过滤器
@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
private final AuthenticationManager authenticationManager;
private final JwtService jwtService;
private final RedisRepository redisRepository;
@SneakyThrows
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
{
try
{
ObjectMapper objectMapper = new ObjectMapper();
LoginDto loginDto = objectMapper.readValue(request.getInputStream(), LoginDto.class);
UsernamePasswordAuthenticationToken authenticationToken
= new UsernamePasswordAuthenticationToken(loginDto.getEmail(), loginDto.getPassword());
return authenticationManager.authenticate(authenticationToken);
}
catch (RuntimeException e)
{
throw new RuntimeException(e.getMessage());
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) throws ServletException, IOException
{
String email = authResult.getName();
List<String> roles = authResult.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
log.info("===successfulAuthentication===");
log.info("authorities = {}", authResult.getAuthorities());
String accessToken = jwtService.generateAccessToken(email, roles);
String refreshToken = jwtService.generateRefreshToken(email);
redisRepository.saveRefreshToken(refreshToken, email);
response.setHeader("Authorization", "Bearer " + accessToken);
response.setHeader("Refresh", refreshToken);
this.getSuccessHandler().onAuthenticationSuccess(request, response, authResult);
}
}
CorsConfiguration
@Configuration
public class CorsConfiguration
{
@Bean
public WebMvcConfigurer corsConfig()
{
return new WebMvcConfigurer()
{
@Override
public void addCorsMappings(CorsRegistry registry)
{
registry.addMapping("/**")
.allowedOriginPatterns("http://localhost:3000", "https://b304-210-121-223-90.ngrok.io")
.allowedMethods(HttpMethod.GET.name(),
HttpMethod.POST.name(),
HttpMethod.OPTIONS.name(),
HttpMethod.PATCH.name(),
HttpMethod.DELETE.name())
.allowCredentials(true)
.allowedHeaders("*");
}
};
}
}
安全日志:TRACE
2023-10-19T11:34:42.070+09:00 INFO 18568 --- [ main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used.
2023-10-19T11:34:43.091+09:00 WARN 18568 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2023-10-19T11:34:43.099+09:00 TRACE 18568 --- [ main] eGlobalAuthenticationAutowiredConfigurer : Eagerly initializing {securityConfig=com.jbaacount.config.SecurityConfig$$SpringCGLIB$$0@27ee8493}
2023-10-19T11:34:43.257+09:00 INFO 18568 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@439a0f0, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@6ac3ce34, org.springframework.security.web.context.SecurityContextHolderFilter@453a699b, org.springframework.security.web.header.HeaderWriterFilter@482bdb02, org.springframework.security.web.authentication.logout.LogoutFilter@6b7ff02a, com.jbaacount.global.security.filter.JwtAuthenticationFilter@6444bf53, com.jbaacount.global.security.filter.JwtVerificationFilter@634a3a2, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6169c15d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@6fc31f6, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4fc3bdb4, org.springframework.security.web.session.SessionManagementFilter@23f2bfdb, org.springframework.security.web.access.ExceptionTranslationFilter@4dac863d, org.springframework.security.web.access.intercept.AuthorizationFilter@15d313d3]
2023-10-19T11:34:43.452+09:00 INFO 18568 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-10-19T11:34:43.459+09:00 INFO 18568 --- [ main] com.jbaacount.ServerApplication : Started ServerApplication in 6.136 seconds (process running for 6.543)
2023-10-19T11:34:49.569+09:00 INFO 18568 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-10-19T11:34:49.576+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@439a0f0, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@6ac3ce34, org.springframework.security.web.context.SecurityContextHolderFilter@453a699b, org.springframework.security.web.header.HeaderWriterFilter@482bdb02, org.springframework.security.web.authentication.logout.LogoutFilter@6b7ff02a, com.jbaacount.global.security.filter.JwtAuthenticationFilter@6444bf53, com.jbaacount.global.security.filter.JwtVerificationFilter@634a3a2, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6169c15d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@6fc31f6, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4fc3bdb4, org.springframework.security.web.session.SessionManagementFilter@23f2bfdb, org.springframework.security.web.access.ExceptionTranslationFilter@4dac863d, org.springframework.security.web.access.intercept.AuthorizationFilter@15d313d3]] (1/1)
2023-10-19T11:34:49.576+09:00 DEBUG 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Securing OPTIONS /members/sign-up
2023-10-19T11:34:49.577+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/13)
2023-10-19T11:34:49.577+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (2/13)
2023-10-19T11:34:49.578+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderFilter (3/13)
2023-10-19T11:34:49.579+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (4/13)
2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (5/13)
2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.s.w.a.logout.LogoutFilter : Did not match request to Or [Ant [pattern='/logout', GET], Ant [pattern='/logout', POST], Ant [pattern='/logout', PUT], Ant [pattern='/logout', DELETE]]
2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking JwtAuthenticationFilter (6/13)
2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking JwtVerificationFilter (7/13)
2023-10-19T11:34:49.580+09:00 INFO 18568 --- [nio-8080-exec-1] c.j.g.s.filter.JwtVerificationFilter : ===shouldNotFilter===
2023-10-19T11:34:49.580+09:00 INFO 18568 --- [nio-8080-exec-1] c.j.g.s.filter.JwtVerificationFilter : authorization = null
2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking RequestCacheAwareFilter (8/13)
2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (9/13)
2023-10-19T11:34:49.581+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (10/13)
2023-10-19T11:34:49.581+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking SessionManagementFilter (11/13)
2023-10-19T11:34:49.581+09:00 TRACE 18568 --- [nio-8080-exec-1] .s.s.w.c.SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]
2023-10-19T11:34:49.582+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]]
2023-10-19T11:34:49.582+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (12/13)
2023-10-19T11:34:49.582+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking AuthorizationFilter (13/13)
2023-10-19T11:34:49.582+09:00 TRACE 18568 --- [nio-8080-exec-1] estMatcherDelegatingAuthorizationManager : Authorizing SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@3e08b966]
2023-10-19T11:34:49.589+09:00 TRACE 18568 --- [nio-8080-exec-1] estMatcherDelegatingAuthorizationManager : Checking authorization on SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@3e08b966] using org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer$$Lambda$1596/0x0000000801c1a730@1b7a1007
2023-10-19T11:34:49.589+09:00 DEBUG 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Secured OPTIONS /members/sign-up
2023-10-19T11:34:49.603+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
我检查了开发工具。这里是标题信息。
enter image description here
enter image description here
我猜请求被发送到shouldNotFilter,但我不知道为什么请求仍然是Options,尽管我允许SecurityConfig中的OPTIONS和PreFlightRequest
如果我错过了什么或应用错误,请让我知道。
1条答案
按热度按时间0sgqnhkj1#
将这行代码添加到控制器的顶部,它将允许您到达端点。