Web on Reactive Stack

1、Spring WebFlux

1.1、Overview

1.2、Reactive Core

1.3、DispatcherHandler

1.4、Annotated Controllers

1.5、Functional Endpoints

1.6、URI Links

1.7、CORS

1.7.1、为什么会出现跨域问题

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。

1.7.2、跨域后的限制

  • 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
  • 无法接触非同源网页的 DOM
  • 无法向非同源地址发送 AJAX 请求

1.7.3、Spring后端解决CORS跨域的方式

对于CORS的跨域请求,主要有以下几种方式可供选择:

  • 返回新的CorsFilter
  • 重写 WebMvcConfigurer
  • 使用注解 @CrossOrigin
  • 手动设置响应头 (HttpServletResponse)
  • 自定web filter 实现跨域

注意事项:

  • CorFilter / WebMvConfigurer / @CrossOrigin 需要 SpringMVC 4.2以上版本才支持,对应springBoot 1.3版本以上
  • 上面前两种方式属于全局 CORS 配置,后两种属于局部 CORS配置。如果使用了局部跨域是会覆盖全局跨域的规则,所以可以通过 @CrossOrigin 注解来进行细粒度更高的跨域资源控制
  • 其实无论哪种方案,最终目的都是修改响应头,向响应头中添加浏览器所要求的数据,进而实现跨域

1.7.3.1、返回新的 CorsFilter(全局跨域)

在任意配置类,返回一个 新的 CorsFIlter Bean ,并添加映射路径和具体的CORS配置路径。

import static org.springframework.http.HttpMethod.DELETE;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.HttpMethod.PUT;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class AppConfiger {

    @Bean
    CorsFilter corsFilter() {
        final CorsConfiguration config = new CorsConfiguration();
        // config.addAllowedOrigin("*");
        config.addAllowedOriginPattern("*");
        config.setAllowCredentials(Boolean.TRUE);
        config.addAllowedMethod(GET);
        config.addAllowedMethod(POST);
        config.addAllowedMethod(PUT);
        config.addAllowedMethod(DELETE);
        config.addAllowedHeader("*");
        config.addExposedHeader("*");

        final UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**", config);

        return new CorsFilter(configSource);
    }

}

1.7.3.2、重写 WebMvcConfigurer(全局跨域)

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfiger implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowCredentials(true)
            // .allowedOrigins("*")
            .allowedOriginPatterns("*")
            .allowedMethods(new String[] { "GET", "POST", "PUT", "DELETE" })
            .allowedHeaders("*")
            .exposedHeaders("*");
    }

}

1.7.3.3、使用注解 (局部跨域)

在控制器类上、方法上使用注解 @CrossOrigin

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.util.DigestUtils.md5DigestAsHex;
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin(
    originPatterns = "*",
    allowCredentials = "true",
    allowedHeaders = "*",
    exposedHeaders = "*",
    methods = { GET, POST, PUT, DELETE }
)
public class IndexController {

    @GetMapping(path = "/", produces = APPLICATION_JSON_VALUE)
    public Map<String, Object> execute() {
        return new HashMap<String, Object>(2) {

            private static final long serialVersionUID = -530647985007348495L;

            {
                put("timestamp", System.currentTimeMillis());
                put("num", md5DigestAsHex(UUID.randomUUID().toString().getBytes(UTF_8)));
            }

        };
    }

}

1.7.3.4、手动设置响应头(局部跨域)

使用 HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.util.DigestUtils.md5DigestAsHex;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @GetMapping(path = "/", produces = APPLICATION_JSON_VALUE)
    public Map<String, Object> execute(HttpServletResponse response) {
        response.addHeader("Access-Control-Allow-Credentials", "true");
        response.addHeader("Access-Control-Allow-Headers", "*");
        response.addHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Expose-Headers", "*");

        return new HashMap<String, Object>(2) {

            private static final long serialVersionUID = -530647985007348495L;

            {
                put("timestamp", System.currentTimeMillis());
                put("num", md5DigestAsHex(UUID.randomUUID().toString().getBytes(UTF_8)));
            }

        };
    }

}

1.7.3.5、使用自定义filter实现跨域

1.7.3.5.1、@Bean注解方式
1.7.3.5.1.1、自定义过滤器
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        final HttpServletResponse resp = (HttpServletResponse) response;
        resp.addHeader("Access-Control-Allow-Credentials", "true");
        resp.addHeader("Access-Control-Allow-Headers", "*");
        resp.addHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
        resp.addHeader("Access-Control-Allow-Origin", "*");
        resp.addHeader("Access-Control-Expose-Headers", "*");

        chain.doFilter(request, resp);
    }

}
1.7.3.5.1.2、Bean注入
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

import com.cnblogs.javalouvre.filter.CorsFilter;

@Configuration
public class FilterConfiger {

    @Bean
    @Order(1)
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        final FilterRegistrationBean<CorsFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new CorsFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setName("corsFilter");

        return filterRegistrationBean;
    }

}
1.7.3.5.2、WebFilter注解方式
1.7.3.5.2.1、自定义过滤器

使用@WebFilter标注当前过滤器

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;

@WebFilter(displayName = "corsFilter", urlPatterns = "/*")
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        final HttpServletResponse resp = (HttpServletResponse) response;
        resp.addHeader("Access-Control-Allow-Credentials", "true");
        resp.addHeader("Access-Control-Allow-Headers", "*");
        resp.addHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
        resp.addHeader("Access-Control-Allow-Origin", "*");
        resp.addHeader("Access-Control-Expose-Headers", "*");

        chain.doFilter(request, resp);
    }

}
1.7.3.5.2.2、注入

使用@ServletComponentScan注解,注入容器

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

import com.cnblogs.javalouvre.filter.CorsFilter;

@ServletComponentScan(basePackageClasses = { CorsFilter.class })
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}

1.8、Web Security

1.9、View Technologies

1.10、HTTP Caching

1.11、WebFlux Config

1.12、HTTP/2

2、WebClient

3、WebSockets

4、Testing

5、RSocket

6、Reactive Libraries

posted @ 2022-11-08 00:31  Bruce.Chang.Lee  阅读(33)  评论(0编辑  收藏  举报