调用spring rest api时出现CORS未经授权的401错误 [英] CORS Unauthorized 401 error when calling spring rest api

查看:232
本文介绍了调用spring rest api时出现CORS未经授权的401错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的rest服务中没有Spring Security实施,并且尝试调用rest资源时遇到了CORS 401未经授权的问题。



I关于它的红色:




I don't have a Spring Security implementation in my rest service, and I'm facing CORS 401 Unauthorized issue when trying to call resources in rest.

I red about it:

https://www.baeldung.com/spring-security-cors-preflight

https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/reference/html5/#cors

I have 2 calls to rest:

1) Login

2) Others

The login functionality is based on Shiro, and when I perform login from Postman for example and then try to call other resource it works.

My problem starts when I started implementing a client side app with react and I'm trying to call the rest from fetch javascript method. I faced CORS issue when calling the login first and I solved it by adding @CorsOrigin annotation to my controller, but after login is succeeding the second call is still failing on cors 401.

If I correctly understand, CORS can be solved by adding filter t oWebSecurityConfig but if my app is working from Postman then there is no need to perform such a change in my server side app and I want to solve it from client side.

That leaves me with the option of passing the JSESSIONID in my requests, right?! I still didn't figure out how to do it... where should I take the JSESSIONID from? I tried reading the cookies of the browser but didn't find it there... I tried taking it from Response headers of the first call to Login but the Response is coming empty!

Tried to solve it with Spring in server side but with no luck:

WebSecurityConfig:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {

//        http
//        .cors();
//        .headers().disable();

//      http
//        .authorizeRequests()
//        .requestMatchers(CorsUtils::isCorsRequest).permitAll()
//        .anyRequest().authenticated()
//        .and().httpBasic()
//        .and().addFilterBefore(new WebSecurityCorsFilter(), ChannelProcessingFilter.class);

        http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);

    }
}

WebSecurityCorsFilter:

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");
    chain.doFilter(request, res);
}

@Override
public void destroy() {
}

}

Or, CorsFilter (using on of them):

@Component
//@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements Filter{

public CorsFilter () {
    super();
}

@Override
public final void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
    final HttpServletResponse response = (HttpServletResponse) res;
    response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");

    // without this header jquery.ajax calls returns 401 even after successful login and SSESSIONID being succesfully stored.
    response.setHeader("Access-Control-Allow-Credentials", "true");

    response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Authorization, Origin, Content-Type, Version");
    response.setHeader("Access-Control-Expose-Headers", "X-Requested-With, Authorization, Origin, Content-Type");

    final HttpServletRequest request = (HttpServletRequest) req;
    if (!request.getMethod().equals("OPTIONS")) {
        chain.doFilter(req, res);
    } else {
        // do not continue with filter chain for options requests
    }
}

@Override
public void destroy() {

} 

@Override
public void init(FilterConfig filterConfig) throws ServletException {       
}
}

解决方案

Thanks to this similar fetch issue: https://github.com/github/fetch/issues/386

I managed finally. So, to summarize:

1) No need for the Spring Security Config changes, except of Adding this annotation to controllers (you can add it just once to a base controller and all can inherit from it)

@CrossOrigin(origins = " * ", allowedHeaders = " * ")

2) From client side, when using fetch I had to use from both requests (Login and the second one):

credentials: 'include', //'same-origin'

(be attention that when using cors don't use the 'same-origin' value of credentials)

No need to set any cookies manually, the browser should handle it (If you need to write a Java client then it's needed... you can search for CookiesManagement java class and you'll find such implementation)

这篇关于调用spring rest api时出现CORS未经授权的401错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆