現在、私は学校ネットワークセンターのプロジェクトを引き継いでいますが、チームメンバーの実際の状況と開発ニーズに基づいて、教師はフロントエンドとバックエンドを完全に分離したいと考えています。バックエンドはJavaを利用してRestful APIをコアとして提供し、フロントエンドはPC、モバイル端末を問わず同じコアを共有できます。初期段階では、認可メカニズムとしての oauth2 などの問題が解決され、大成功だと思いました。 (最近、oauth2を実装するSpring sercurityのソリューションを詳しく紹介する予定です) その結果、またしてもクロスドメインの問題が発生し、大きな落とし穴に足を踏み入れてしまったので、今後のトラブルを避けるためにここに記録しておきます。
エラーメッセージは次のとおりです:
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 しか使用できません。詳細については、Web アプリケーションのクロスドメイン アクセス ソリューションの概要をご覧ください
CORS プロトコル
現在、JS が世界を席巻する傾向にあり、ほとんどのアプリケーションにとってブラウザが最適な場所となっています。モバイル側でも、さまざまな Hybird ソリューションがあり、ローカル ファイル システムの Web ページでは、外部データを取得するニーズもあり、これらのニーズはクロスドメインでなければなりません。クロスドメイン ソリューションを探していたとき、最も洗練されたソリューションは、HTML5 によってもたらされた「クロスオリジン リソース共有」の新機能であることがわかりました。これにより、開発者はドメインを越えてリソースにアクセスできるかどうかを決定する権限が得られます。
CORS は W3C 標準であり、正式名称は「Cross-Origin Resource Sharing」です。
これにより、ブラウザーがクロスオリジン サーバーに 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」は、クロスドメイン リクエストを許可することを示します リクエストにはコンテンツ タイプ ヘッダーが含まれています
もちろん、この問題に対処する場合、フロントエンドには多くの落とし穴があります。特に Chrome ブラウザでは、ローカルホストからのクロスドメイン リクエストが許可されません。詳細な解決策については、私のプロジェクトのフロントエンド ソリューションを担当するパートナーに問い合わせてください。リンクがあるふりをします
一般的な解決策
問題の原因とそれをサポートする解決策はわかっています。今すぐ解決しましょう。考え方は非常に簡単です。フロントエンドがクロスドメイン リソースを要求する場合、それに応答ヘッダーを追加するだけです。明らかに、自分でフィルターを定義するのが最も簡単です。
/** * 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>