SpringBoot解决跨域问题

一、同源策略

  1. 同源,就是咱们域名、端口号、ip、采用的协议都相同,那么我们就是同源的
  2. 反之就是不同源的!!!
  3. 出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
  4. 所以,用最简单的话来说,就是前端可以发请求给服务器,服务器也可以进行响应,只是因为浏览器会对请求头进行判断,所以要么前端设置请求头,要么后端设置请求头

在这里插入图片描述

不同源的应用场景:

  1. 本地文件,向远程服务器发送请求,可以发送,但是会出现跨域
  2. 本地服务器跑前端文件,服务器跑服务器程序,也会出现跨域问题

二、跨域问题

跨域报错如下:

Access to XMLHttpRequest at 'http://localhost:8080/t1' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
test1.html?_ijt=aekdfma33ut4n31cgsohdrjt89:17 {readyState: 0, getResponseHeader: ƒ, getAllResponseHeaders: ƒ, setRequestHeader: ƒ, overrideMimeType: ƒ, …}
jquery-1.9.1.min.js:5          GET http://localhost:8080/t1 net::ERR_FAILED 200

三、spring boot解决跨域问题

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

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

1、使用CorsFilter

注意:

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

报错:

java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.

错误原因:2.4.0之后如果写.allowedOrigins(*)会报错

替换成.allowedOriginPatterns即可。


@Configuration
public class corsFilter {
    @Bean
    public CorsFilter CorsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();

        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);


        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);

        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

2、实现WebMvcConfigurer里面的addCorsMappings方法

@Configuration
public class corsFilter1 implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")  // 匹配所有的路径
                .allowCredentials(true) // 设置允许凭证
                .allowedHeaders("*")   // 设置请求头
                .allowedMethods("GET","POST","PUT","DELETE") // 设置允许的方式
                .allowedOriginPatterns("*");
    }
}

3、@CrossOrigin局部跨域通过

@GetMapping("/t2")
@CrossOrigin
public Map t2() {
    HashMap<String, Object> map = new HashMap<>();
    User user = new User();
    user.setUsername("123456");
    user.setPassword("程世玉");
    map.put("user",user);

    return map;
}

也可以指定允许通过的ip

/**
 * 如果只是想部分接口跨域,且不想使用配置来管理的话,可以使用这种方式
 * 添加响应头解决跨域
 * @return
 */
@RequestMapping(value = "/user_2", method = RequestMethod.POST)
@CrossOrigin(origins = "http://172.16.71.27:8080", maxAge = 3600)
public User getUser_2(@RequestParam Long id) {
    return new User(id, "Booker", "admin", "sdfsdkjf93hu8dvn");
}

4、添加响应头解决跨域

@RequestMapping(value = "/user-1")
public User getUser_1(HttpServletResponse response ) {

    // 允许所有,不安全
    response.addHeader("Access-Control-Allow-Origin", "*");
    response.addHeader("Access-Control-Max-Age", "10");
    response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT");
    response.setHeader("Access-Control-Allow-Credentials", "true");

    
    return new User(1L, "Booker", "admin", "sdfsdkjf93hu8dvn");
}

5、ajax跨域访问增加响应头

$.ajax({
    url: "http://xxxx.xxxx.com/api/user/user-1",
    type: "post",
    dataType: "text",
    contentType: "application/json",
    data: JSON.stringify(data),
    headers: {'Content-Type': 'application/json'},
    success: function (res) {
        alert(res);
    }
})

6、手写反向代理解决跨域问题

@SpringBootApplication
public class SpringBoot1DayApplication {

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


    @Resource
    private RestTemplateBuilder restTemplateBuilder;

    @Bean
    public RestTemplate restTemplate() {
        return restTemplateBuilder.build();
    }



}

代理类:通过restTemplate,模拟发请求,等于说我们只要暴露这一个接口就可以,其余接口统统可以通过这个接口进行访问!!!

核心方法:

@RequestMapping("/api/**")
@CrossOrigin
public Object t3(HttpServletRequest request) {
    String url = "http://localhost:8080";
    return restTemplate.getForObject( proxyAddress+ request.getRequestURI().replace("/api", ""), Object.class);
}

全部代码

package com.example.springboot1_day.Handler;

import com.example.springboot1_day.eneity.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 程世玉
 * @create 2022/3/23 21:32
 * @PROJECT_NAME SpringBoot-Homology
 * @Description
 */
@RestController
public class UserHandler {

    @Value("${proxy.address}")
    private String proxyAddress;

    @Resource
    private RestTemplate restTemplate;


    @GetMapping("/t1")
    public Map t1() {
        HashMap<String, Object> map = new HashMap<>();
        User user = new User();
        user.setUsername("123456");
        user.setPassword("程世玉");
        map.put("user",user);

        return map;
    }


    @GetMapping("/t2")
    public Map t2() {
        HashMap<String, Object> map = new HashMap<>();
        User user = new User();
        user.setUsername("123456");
        user.setPassword("程世玉");
        map.put("user",user);

        return map;
    }


    @RequestMapping("/api/**")
    @CrossOrigin
    public Object t3(HttpServletRequest request) {
        String url = "http://localhost:8080";
        return restTemplate.getForObject( proxyAddress+ request.getRequestURI().replace("/api", ""), Object.class);
    }

}

所有解决跨域问题,不外乎就是解决浏览器拦截问题,要么前端设置请求头,要么后端设置请求头,无论谁设置请求头,浏览器只要放行即可

五、SpringBoot中RestTemplate

spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。

在日常项目开发中,有时候我们需要调用第三方接口数据,常用的方法有传统JDK自带的URLConnection,Apache Jakarta Common下的子项目HttpClient ,Spring的RestTemplate。

这么一解释,就明白了RestTemplate是什么了,就是一个类似于HttpClient一样的框架,封装了一些get请求,post请求,put请求等等请求的方法,用来模拟请求,让我们可以通过Java程序想其他不同端口的服务接口访问数据。

1、RestTemplate API使用

postForEntity()(有请求体)

//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
headers.add("key1","value1");
headers.add("key2","value2");

HttpEntity<User> entity = new HttpEntity<>(new User(), headers);

String url = "http://xxx.com";
//发送post请求
ResponseEntity<R> responseEntity = restTemplate.postForEntity(url, entity, R.class);
R r = responseEntity.getBody();
if (HttpStatus.OK.value() != r.getStatus()) {
    log.error("发送错误:{}", r.getMsg());
}

postForEntity()(有请求参数)

String url="http://xxx.com??p1={1}&p2={2}";

ResponseEntity<Map> responseEntity = restTemplate.postForEntity(
                url,
                null,
                Map.class,
                "aa","bb");

postForObject()

//参数是Book类型,返回值也是Book类型
Book book = restTemplate.postForObject("http://127.0.0.1:8080/updatebook", book, Book.class);

post方法获取List数据

List<ProductTypeVO> voList = Lists.newArrayList();

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
headers.add("key1","value1");
headers.add("key2","value2");

HttpEntity<String> entity = new HttpEntity<>("", headers);
ProductTypeVO[] responseEntity = restTemplate.postForObject(
        url,
        entity,
        ProductTypeVO[].class);

if (null != responseEntity) {
    voList = Arrays.asList(responseEntity);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值