Jetzt übernehme ich ein Projekt im Schulnetzwerkzentrum, basierend auf der tatsächlichen Situation und den Entwicklungsbedürfnissen der Teammitglieder, hofft der Lehrer, das Front- und Back-End vollständig zu trennen. Das Backend verwendet Java, um eine restful API als Kern bereitzustellen, und das Frontend kann unabhängig vom PC oder mobilen Endgerät denselben Kern teilen. In der Anfangsphase wurden Probleme wie oauth2 als Autorisierungsmechanismus gelöst, und ich dachte, es würde ein großer Erfolg werden. (Vor kurzem habe ich geplant, die Lösung der Verwendung von Spring Security zur Implementierung von oauth2 im Detail vorzustellen.) Infolgedessen ist ein weiteres domänenübergreifendes Problem aufgetreten, das uns in eine große Grube geführt hat. Wir werden es hier aufzeichnen, um zukünftige Probleme zu vermeiden.
Die Fehlermeldung lautet wie folgt:
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.
Was ist domänenübergreifend?
Einfach ausgedrückt beschränkt der Browser den Zugriff auf den JS-Code unter Site A auf die URL unter Site B. Stellen Sie eine Ajax-Anfrage. Wenn der Front-End-Domänenname beispielsweise www.abc.com lautet, ist der Zugriff des in der aktuellen Umgebung ausgeführten JS-Codes aus Sicherheitsgründen auf Ressourcen unter dem Domänennamen www.xyz.com beschränkt. Moderne Browser blockieren aus Sicherheitsgründen standardmäßig domänenübergreifende Ajax-Anfragen. Dies ist eine notwendige Funktion in modernen Browsern, bringt jedoch häufig Unannehmlichkeiten für die Entwicklung mit sich. Besonders für einen Backend-Entwickler wie mich ist diese Sache einfach magisch.
Aber der Bedarf an domänenübergreifendem Arbeiten war schon immer vorhanden. Um domänenübergreifend zu arbeiten, haben sich fleißige und mutige Programmierer viele Methoden ausgedacht, wie z. B. JSONP, Proxy-Dateien usw. Diese Praktiken verursachen jedoch viele unnötige Wartungskosten und es gibt viele Einschränkungen in Anwendungsszenarien. Beispielsweise ist jsonP nicht XHR, sodass jsonP nur GET zum Übergeben von Parametern verwenden kann. Ausführlichere Informationen finden Sie hier. Zusammenfassung der domänenübergreifenden Zugriffslösungen für Webanwendungen
CORS-Protokoll
Das heutige JS hat die Tendenz, die Welt zu dominieren, und Browser sind zum besten Zuhause dafür geworden die meisten Anwendungen. Auch auf der mobilen Seite gibt es verschiedene Hybird-Lösungen. Auf der Webseite des lokalen Dateisystems müssen auch externe Daten abgerufen werden, und diese Anforderungen müssen domänenübergreifend sein. Als ich nach einer domänenübergreifenden Lösung suchte, stellte ich fest, dass die eleganteste Lösung die neue Funktion „Cross-Origin Resource Sharing“ von HTML5 ist, die Entwicklern die Möglichkeit gibt, zu entscheiden, ob domänenübergreifend auf Ressourcen zugegriffen werden darf.
CORS ist ein W3C-Standard, sein vollständiger Name lautet „Cross-Origin Resource Sharing“.
Es ermöglicht dem Browser, XMLHttpRequest-Anfragen an Cross-Origin-Server zu senden, wodurch die Einschränkung überwunden wird, dass AJAX nur von demselben Ursprung aus verwendet werden kann.
Warum ist es elegant?
Der gesamte CORS-Kommunikationsprozess wird automatisch vom Browser abgeschlossen und erfordert keine Benutzerbeteiligung. Für Entwickler gibt es keinen Unterschied zwischen CORS-Kommunikation und AJAX-Kommunikation aus derselben Quelle, und der Code ist genau derselbe. Sobald der Browser feststellt, dass die AJAX-Anfrage ursprungsübergreifend ist, fügt er automatisch einige zusätzliche Header-Informationen hinzu, und manchmal wird eine zusätzliche Anfrage gestellt, aber der Benutzer wird dies nicht spüren.
Daher ist der Server der Schlüssel zum Erreichen der CORS-Kommunikation. Solange der Server die CORS-Schnittstelle implementiert, kann er quellenübergreifend kommunizieren.
Der Schlüssel zur Lösung dieses Problems liegt bei mir, dem Programmierer, der für das Backend verantwortlich ist.
Es ist nicht schwer, sich die Dokumentation anzusehen. Sie müssen lediglich Access-Control-Allow-Origin im HTTP-Header festlegen, um zu bestimmen, welche Websites darauf zugreifen dürfen. Weitere Informationen zum CROS-Protokoll finden Sie unter „Cross-Domain Resource Sharing CORS“. Detaillierte Erklärung
Gemeinsame CROS-Header
CORS verfügt über die folgenden gemeinsamen Header
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“ gibt an, dass es „http://kbiao.me“ erlaubt, domänenübergreifende Anfragen zu initiieren
„Access-Control-Max-Age“ gibt an, dass dies innerhalb von 3628800 Sekunden der Fall ist Es ist nicht erforderlich, Vorabprüfungsanfragen zu senden, und die Anfrage kann zwischengespeichert werden. Ergebnisse (aus den obigen Informationen wissen wir, dass eine AJAX-Anfrage im CROS-Protokoll in die OPTION-Vortestanfrage für den ersten Schritt und die formelle Anfrage unterteilt ist)
„Access-Control-Allow-Methods“ gibt an, dass es GET-, PUT- und DELETE-Anfragen externer Domänen zulässt.
„Access-Control-Allow-Headers“ gibt an, dass es domänenübergreifende Anfragen zulässt Content-Type-Header
Natürlich gibt es im Frontend viele Fallstricke, insbesondere der Chrome-Browser lässt keine domänenübergreifenden Anfragen von localhost zu Partner, der für die Frontend-Lösung in meinem Projekt verantwortlich ist. Stellen Sie sich vor, es gäbe einen Link
Allgemeine Lösung
Da wir nun die Ursache des Problems und die unterstützenden Lösungen kennen, können wir es jetzt lösen. Die Idee ist sehr einfach. Wenn das Frontend domänenübergreifende Ressourcen anfordert, fügen wir einfach den Antwortheader hinzu. Offensichtlich ist es für uns am einfachsten, einen Filter selbst zu definieren.
/** * 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>