이제 저는 학교 네트워크 센터의 프로젝트를 맡고 있습니다. 선생님은 팀원들의 실제 상황과 개발 요구 사항을 토대로 프론트엔드와 백엔드를 완전히 분리하기를 원하십니다. 백엔드는 java를 사용하여 Restful API를 코어로 제공하며, 프론트엔드는 PC, 모바일 단말에 관계없이 동일한 코어를 공유할 수 있습니다. 초기에는 인증 메커니즘으로 oauth2 등의 문제가 해결됐고, 큰 성공을 거둘 것이라고 생각했다. (최근에는 Spring 보안을 사용하여 oauth2를 구현하는 솔루션을 자세히 소개할 예정입니다.) 결과적으로 또 다른 크로스 도메인 문제가 발생하여 향후 문제를 피하기 위해 여기에 기록하겠습니다.
오류 메시지는 다음과 같습니다.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.
교차 도메인이란 무엇입니까
간단히 말하면 브라우저는 사이트 A의 js 코드에 대한 액세스를 다음으로 제한합니다. 사이트 B 아래의 URL Ajax 요청을 합니다. 예를 들어 프런트 엔드 도메인 이름이 www.abc.com인 경우 현재 환경에서 실행 중인 js 코드는 보안상의 이유로 www.xyz.com 도메인 이름 아래의 리소스에 액세스하는 것이 제한됩니다. 최신 브라우저는 보안상의 이유로 기본적으로 도메인 간 Ajax 요청을 차단합니다. 이는 최신 브라우저에 필요한 기능이지만 개발에 불편을 초래하는 경우가 많습니다. 특히 나 같은 백엔드 개발자에게 이것은 정말 마법같은 일이다.
그러나 크로스 도메인의 필요성은 항상 존재해 왔습니다. 크로스 도메인을 위해 열심히 일하고 용감한 프로그래머들은 jsonP, 프록시 파일 등과 같은 다양한 방법을 생각해냈습니다. 그러나 이러한 관행으로 인해 불필요한 유지 관리 비용이 많이 추가되고 애플리케이션 시나리오에 많은 제한이 있습니다. 예를 들어 jsonP는 XHR이 아니므로 jsonP는 GET을 사용하여 매개 변수를 전달할 수만 있습니다. 자세한 내용은 여기에서 확인할 수 있습니다. 웹 애플리케이션용 크로스 도메인 액세스 솔루션 요약
CORS 프로토콜
오늘날의 JS는 세계를 지배하는 경향이 있으며 브라우저는 최고의 홈이 되었습니다. 대부분의 응용 프로그램 장소. 모바일 측면에서도 다양한 Hybird 솔루션이 존재하는데, 로컬 파일 시스템의 웹 페이지에서도 외부 데이터를 얻어야 하는데, 이러한 요구는 크로스 도메인이어야 합니다. 교차 도메인 솔루션을 찾을 때 가장 우아한 솔루션은 개발자가 도메인 전체에서 리소스에 액세스할 수 있는지 여부를 결정할 수 있는 권한을 제공하는 HTML5에서 가져온 "Cross-Origin Resource Sharing"의 새로운 기능이라는 것을 알았습니다.
CORS는 W3C 표준이며 전체 이름은 "교차 출처 리소스 공유"입니다.
브라우저가 교차 출처 서버에 XMLHttpRequest 요청을 발행할 수 있도록 함으로써 AJAX가 동일한 출처에서만 사용될 수 있다는 한계를 극복합니다.
왜 우아한가요?
전체 CORS 통신 프로세스는 브라우저에 의해 자동으로 완료되며 사용자 참여가 필요하지 않습니다. 개발자의 경우 동일한 소스의 CORS 통신과 AJAX 통신 간에 차이가 없으며 코드도 완전히 동일합니다. 브라우저가 AJAX 요청이 교차 출처임을 발견하면 자동으로 추가 헤더 정보를 추가하고 때로는 추가 요청이 이루어지지만 사용자는 이를 느끼지 못할 것입니다.
따라서 CORS 통신을 이루기 위한 핵심은 서버입니다. 서버가 CORS 인터페이스를 구현하는 한 소스 간에 통신할 수 있습니다.
이 문제의 해결의 열쇠는 백엔드를 담당하는 프로그래머인 나에게 달려있습니다.
문서를 보는 것은 어렵지 않습니다. 액세스를 허용해야 하는 사이트를 결정하려면 http 헤더에 Access-Control-Allow-Origin을 설정하기만 하면 됩니다. CROS 프로토콜에 대한 자세한 내용은 도메인 간 리소스 공유 CORS 자세한 설명
CROS 공통 헤더
CORS에는 다음과 같은 공통 헤더가 있습니다
Access-Control-Allow-Origin: http://kbiao.me Access-Control-Max-Age: 3628800 Access-Control-Allow-Methods: GET,PUT, DELETE Access-Control-Allow-Headers: content-type
를 참조하세요. Access-Control-Allow-Origin"은 "http://kbiao.me"가 도메인 간 요청을 시작할 수 있음을 나타냅니다.
"Access-Control-Max-Age"는 3628800초 내에 더 이상 요청이 허용되지 않음을 나타냅니다. 사전 검사 요청이 필요하며 해당 요청을 캐싱할 수 있습니다. 결과 (위 정보를 통해 CROS 프로토콜에서는 AJAX 요청이 첫 번째 단계 OPTION 사전 테스트 요청과 정식 요청으로 나누어져 있음을 알 수 있습니다)
"Access-Control-Allow-Methods"는 GET, PUT 및 DELETE 외부 도메인 요청을 허용함을 나타냅니다.
"Access-Control-Allow-Headers"는 콘텐츠를 포함하는 도메인 간 요청을 허용함을 나타냅니다. type header
물론 이 문제를 처리할 때 프런트 엔드에는 많은 함정이 있습니다. 특히 Chrome 브라우저는 localhost의 도메인 간 요청을 허용하지 않습니다. 자세한 해결 방법은 담당 파트너에게 문의하세요. 내 프로젝트의 프런트 엔드 솔루션을 위해. 링크가 있는 척
일반 해결 방법
이제 문제의 원인과 지원 방법을 알았으니 이제 해결해 보겠습니다. 아이디어는 매우 간단합니다. 프런트 엔드가 도메인 간 리소스를 요청할 때 응답 헤더를 추가하기만 하면 됩니다. 분명히 필터를 직접 정의하는 것이 가장 쉽습니다.
/** * Created by kangb on 2016/5/10. */ @Component public class myCORSFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) servletResponse; String origin = (String) servletRequest.getRemoteHost()+":"+servletRequest.getRemotePort(); response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization"); response.setHeader("Access-Control-Allow-Credentials","true"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
@Component 是Spring的注解,关键部分在doFilter中,添加了我们需要的头, option 是预检测需要所以需要允许, Authorization 是做了oauth2登录响应所必须的, Access-Control-Allow-Credentials 表示允许cookies。都是根据自己项目的实际需要配置。
再配置Web.xml使得过滤器生效
<filter> <filter-name>cors</filter-name> <filter-class>·CLASS_PATH·.myeCORSFilter</filter-class> </filter> <filter-mapping> <filter-name>cors</filter-name> <url-pattern>/api/*</url-pattern> </filter-mapping>
接下来前端就可以像往常一样使用AJAX请求获得资源了,完全不需要做出什么改变。
SPRING 4中更优雅的办法
SpringMVC4提供了非常方便的实现跨域的方法。在requestMapping中使用注解。
@CrossOrigin(origins = “http://kbiao.me”)
全局实现 .定义类继承WebMvcConfigurerAdapter,设置跨域相关的配置
public class CorsConfigurerAdapter extends WebMvcConfigurerAdapter{ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/*").allowedOrigins("*"); } }
将该类注入到容器中:
<bean class="com.tmall.wireless.angel.web.config.CorsConfigurerAdapter"></bean>