最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring @CrossOrigin 注解原理實(shí)現(xiàn)

 更新時(shí)間:2020年07月08日 11:54:46   作者:暗中觀察  
這篇文章主要介紹了Spring @CrossOrigin 注解原理實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

現(xiàn)實(shí)開發(fā)中,我們難免遇到跨域問題,以前筆者只知道jsonp這種解決方式,后面聽說spring只要加入@CrossOrigin即可解決跨域問題。本著好奇的心里,筆者看了下@CrossOrigin 作用原理,寫下這篇博客。

先說原理:其實(shí)很簡(jiǎn)單,就是利用spring的攔截器實(shí)現(xiàn)往response里添加 Access-Control-Allow-Origin等響應(yīng)頭信息,我們可以看下spring是怎么做的

注:這里使用的spring版本為5.0.6

我們可以先往RequestMappingHandlerMapping 的initCorsConfiguration方法打一個(gè)斷點(diǎn),發(fā)現(xiàn)方法調(diào)用情況如下

如果controller在類上標(biāo)了@CrossOrigin或在方法上標(biāo)了@CrossOrigin注解,則spring 在記錄mapper映射時(shí)會(huì)記錄對(duì)應(yīng)跨域請(qǐng)求映射,代碼如下

RequestMappingHandlerMapping
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
 HandlerMethod handlerMethod = createHandlerMethod(handler, method);
 Class<?> beanType = handlerMethod.getBeanType();
    //獲取handler上的CrossOrigin 注解
 CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(beanType, CrossOrigin.class);
    //獲取handler 方法上的CrossOrigin 注解
 CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
    
 if (typeAnnotation == null && methodAnnotation == null) {
      //如果類上和方法都沒標(biāo)CrossOrigin 注解,則返回一個(gè)null
  return null;
 }
    //構(gòu)建一個(gè)CorsConfiguration 并返回
 CorsConfiguration config = new CorsConfiguration();
 updateCorsConfig(config, typeAnnotation);
 updateCorsConfig(config, methodAnnotation);

 if (CollectionUtils.isEmpty(config.getAllowedMethods())) {
  for (RequestMethod allowedMethod : mappingInfo.getMethodsCondition().getMethods()) {
  config.addAllowedMethod(allowedMethod.name());
  }
 }
 return config.applyPermitDefaultValues();
 }

將結(jié)果返回到了AbstractHandlerMethodMapping#register,主要代碼如下

 CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
  if (corsConfig != null) {
//會(huì)保存handlerMethod處理跨域請(qǐng)求的配置
   this.corsLookup.put(handlerMethod, corsConfig);
  }

當(dāng)一個(gè)跨域請(qǐng)求過來時(shí),spring在獲取handler時(shí)會(huì)判斷這個(gè)請(qǐng)求是否是一個(gè)跨域請(qǐng)求,如果是,則會(huì)返回一個(gè)可以處理跨域的handler

AbstractHandlerMapping#getHandler 
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
 //如果是一個(gè)跨域請(qǐng)求
if (CorsUtils.isCorsRequest(request)) {
    //拿到跨域的全局配置
  CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
     //拿到hander的跨域配置
  CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
  CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
      //處理跨域(即往響應(yīng)頭添加Access-Control-Allow-Origin信息等),并返回對(duì)應(yīng)的handler對(duì)象
  executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
 }

我們可以看下如何判定一個(gè)請(qǐng)求是一個(gè)跨域請(qǐng)求,

public static boolean isCorsRequest(HttpServletRequest request) {
//判定請(qǐng)求頭是否有Origin 屬性即可
 return (request.getHeader(HttpHeaders.ORIGIN) != null);
 }

再看下getCorsHandlerExecutionChain 是如何獲取一個(gè)handler

 protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
  HandlerExecutionChain chain, @Nullable CorsConfiguration config) {

 if (CorsUtils.isPreFlightRequest(request)) {
  HandlerInterceptor[] interceptors = chain.getInterceptors();
  chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
 }
 else {
      //只是給執(zhí)行器鏈添加了一個(gè)攔截器
  chain.addInterceptor(new CorsInterceptor(config));
 }
 return chain;
 }

也就是在調(diào)用目標(biāo)方法前會(huì)先調(diào)用CorsInterceptor#preHandle,我們觀察得到其也是調(diào)用了corsProcessor.processRequest方法,我們往這里打個(gè)斷點(diǎn)

processRequest方法的主要邏輯如下

 public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,
  HttpServletResponse response) throws IOException {
    //....
    //調(diào)用了自身的handleInternal方法
 return handleInternal(serverRequest, serverResponse, config, preFlightRequest);
 }


protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
  CorsConfiguration config, boolean preFlightRequest) throws IOException {

 String requestOrigin = request.getHeaders().getOrigin();
 String allowOrigin = checkOrigin(config, requestOrigin);
 HttpHeaders responseHeaders = response.getHeaders();

 responseHeaders.addAll(HttpHeaders.VARY, Arrays.asList(HttpHeaders.ORIGIN,
  HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS));

 if (allowOrigin == null) {
  logger.debug("Rejecting CORS request because '" + requestOrigin + "' origin is not allowed");
  rejectRequest(response);
  return false;
 }

 HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
 List<HttpMethod> allowMethods = checkMethods(config, requestMethod);
 if (allowMethods == null) {
  logger.debug("Rejecting CORS request because '" + requestMethod + "' request method is not allowed");
  rejectRequest(response);
  return false;
 }

 List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
 List<String> allowHeaders = checkHeaders(config, requestHeaders);
 if (preFlightRequest && allowHeaders == null) {
  logger.debug("Rejecting CORS request because '" + requestHeaders + "' request headers are not allowed");
  rejectRequest(response);
  return false;
 }
    //設(shè)置響應(yīng)頭
 responseHeaders.setAccessControlAllowOrigin(allowOrigin);

 if (preFlightRequest) {
  responseHeaders.setAccessControlAllowMethods(allowMethods);
 }

 if (preFlightRequest && !allowHeaders.isEmpty()) {
  responseHeaders.setAccessControlAllowHeaders(allowHeaders);
 }

 if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {
  responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());
 }

 if (Boolean.TRUE.equals(config.getAllowCredentials())) {
  responseHeaders.setAccessControlAllowCredentials(true);
 }

 if (preFlightRequest && config.getMaxAge() != null) {
  responseHeaders.setAccessControlMaxAge(config.getMaxAge());
 }
    //刷新
 response.flush();
 return true;
 }

至此@CrossOrigin的使命就完成了,說白了就是用攔截器給response添加響應(yīng)頭信息而已

到此這篇關(guān)于Spring @CrossOrigin 注解原理實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Spring @CrossOrigin 注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

昭觉县| 阳江市| 临颍县| 蓬莱市| 怀集县| 祁门县| 平顶山市| 鄄城县| 城口县| 辰溪县| 通化县| 石阡县| 阳朔县| 舟山市| 萨嘎县| 三门县| 志丹县| 博兴县| 桐城市| 恩平市| 攀枝花市| 宝山区| 大足县| 丰镇市| 马边| 老河口市| 邳州市| 南江县| 新河县| 健康| 德钦县| 陈巴尔虎旗| 桓仁| 玛曲县| 买车| 泸水县| 蓬安县| 隆安县| 弋阳县| 鄂尔多斯市| 专栏|