如何在 Spring MVC 中为错误响应启用 CORS?

Posted

技术标签:

【中文标题】如何在 Spring MVC 中为错误响应启用 CORS?【英文标题】:How to enable CORS for Error Response in Spring MVC? 【发布时间】:2018-06-17 22:11:12 【问题描述】:

我正在开发应用程序,我将 Spring MVC 用于后端,将 Angular5 用于前端。我一直坚持实施 Auth2 安全层,包括跨域资源共享。我的 CORS 过滤器实现如下所示:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@WebFilter("/*")
public class WebSecurityCorsFilter implements Filter 

    @Override
    public void init(FilterConfig filterConfig) throws ServletException 
    

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
        HttpServletResponse res = (HttpServletResponse) response;
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
        res.setHeader("Access-Control-Max-Age", "3600");
        res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, x-requested-with, Cache-Control");


        if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) request).getMethod())) 
            res.setStatus(HttpServletResponse.SC_OK);
         else 
            chain.doFilter(request, res);
        

    
    @Override
    public void destroy() 
    

我的工作几乎正常,我能够获取 access_token 并使用它从 ResourcesServer 获取受保护的数据:

"access_token":"4fcef1f8-4306-4047-9d4d-1c3cf74ecc44","token_type":"bearer","re​​fresh_token":"397016eb-dfb0-4944-a2e0-50c3bd07c250","expires_in":29, “范围”:“读取 写信任”

Browser console screenshot

当我尝试使用过期令牌处理请求时,问题就开始了。在这种情况下,我无法通过 Angular 捕获正确的 ErrorResponeCode。而不是 401,我 Angular HttpClient 得到了状态为 0 的“未知错误”。

看起来问题出在 CORS 策略上,其中 ErrorResponse 不包含诸如 Access-Control-Allow-Origin (...) 之类的必要标头

无法加载http://localhost:8030/api/xxxx:否 'Access-Control-Allow-Origin' 标头出现在请求的 资源。因此不允许使用原点“http://localhost:8070” 使用权。响应的 HTTP 状态代码为 401。

ErrorResponse Headers - Screenshot

我已经搜索了如何在 Spring MVC 中为 ErorrResponse(InvalidTokenException 等)启用 CORS。我尝试了各种方法:accessDeniedHandler 和 setExceptionTranslator 但没有成功。我真的很努力自己找到解决方案,但我是 Spring 的初学者。我不确定这是否可能。

角度(更新)

@hrdkisback,这不是角度问题,无论如何这是我的代码:

@Injectable()

export class HttpInterceptorService implements HttpInterceptor 


    addToken(req: HttpRequest<any>, oauthService: AuthenticationService): HttpRequest<any> 

        if(oauthService.isTokenExist())

            return req.clone( setHeaders:  Authorization: 'Bearer ' + oauthService.getAccessToken() )
        

        return req;
    


    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> 

        let oauthService = this.inj.get(AuthenticationService);

        return next.handle(this.addToken(req,oauthService))

        .do((event: HttpEvent<any>) => 

            if (event instanceof HttpResponse) 

                // process successful responses here

            
        , (error: any) =>  

            if (error instanceof HttpErrorResponse) 

                // Error 

                console.log(error);
            
        );

    

【问题讨论】:

【参考方案1】:

我遇到了同样的问题..我正在尝试使用 Angular 5 进行基本身份验证。 问题是您没有在错误响应中添加 CORS 标头。 这就是我所做的

@Component
public class AuthEntryPoint extends BasicAuthenticationEntryPoint 

@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
  throws IOException, ServletException 
    response.addHeader("WWW-Authenticate", "Basic realm=" +getRealmName());
    response.addHeader("Access-Control-Allow-Origin", "http://localhost:4200");
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    PrintWriter writer = response.getWriter();
    writer.println("HTTP Status 401 - " + authEx.getMessage());
    

这样就行了!

【讨论】:

【参考方案2】:

在 ResourcesServer 配置级别添加我的 CORS 过滤器后问题解决了,如下所示:

适合我的正确配置!

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter 


    @Override
        public void configure(HttpSecurity http) throws Exception 

            http
            .addFilterAfter(new WebSecurityCorsFilter(), CsrfFilter.class)
            ...
        
   ....

在我之前的配置中,我以相同的方式添加了过滤器,但在 MVC 安全配置的顶层,这是我问题的根源:

导致我的问题的先前配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter 



    @Override
            public void configure(HttpSecurity http) throws Exception 

                http
                .addFilterAfter(new WebSecurityCorsFilter(), CsrfFilter.class)
                ...
            
       ....
    

【讨论】:

以上是关于如何在 Spring MVC 中为错误响应启用 CORS?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 spring-mvc 中为 REST 查询提供对象列表?

如何在 Spring Boot / Spring Data 中为 Amazon RDS Mysql 启用 SSL?

如何在spring mvc中启用ZuulProxy

如何仅在 Spring 中为微服务项目的外部客户端启用身份验证?

如何使用 Spring MVC 和 Spring Security 为资源处理程序启用 HTTP 缓存

如何在 Spring MVC 中管理错误消息