同源策略与跨域

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

不论个人练习还是实际开发,我们都不可避免地会遇到跨域问题,而造成跨域的罪魁祸首就是浏览器的同源策略。要解决跨域,首先要了解同源策略。

主要内容

  • 同源策略与跨域
  • 解决跨域
    • JSONP
    • CORS

同源策略与跨域

同源策略

百度“同源策略”得到以下回答:

同源策略,它是由Netscape提出的一个著名的安全策略。所有支持JavaScript 的浏览器都会使用这个策略。

所谓同源是指,域名,协议,端口相同。

在一个浏览器的两个tab页中分别打开百度和谷歌的页面,当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。

同源策略是浏览器的行为,是浏览器为了保护本地数据不被JavaScript代码获取回来的数据污染而做出的拦截行为。即请求发送了,服务器响应了,但是无法被浏览器接收。

简单来说,就是:

很多人以为同源策略是浏览器不让请求发出去、或者后端拒绝返回数据。NO!实际情况是,请求正常发出,后端接口也正常响应,只不过数据到了浏览器后被丢弃了。


同源策略限制内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM节点
  • AJAX跨域请求的数据

以下情况都属于跨域:

跨域原因说明

示例

域名不同

www.jd.com

www.taobao.com

域名相同,端口不同

www.jd.com:8080

www.jd.com:8081

二级域名不同

item.jd.com

miaosha.jd.com

协议不同

HTTP与HTTPS

简单来说,是否跨域的3个因素为:协议、域名、端口。

需要注意,如果协议、域名、端口都相同,但是请求路径不同,不属于跨域,如:

https://www.jd.com/item

https://www.jd.com/goods

而上面示意图中,在manage.leyou.com的页面访问api.leyou.com的接口,由于二级域名不同,导致跨域。从这个角度来看,只要是前后端分离的项目,必然跨域!(即使部署在同一个服务器,前后端项目端口肯定不同)

跨域Demo

随手建一个SpringBoot项目后,把下面的文件拷过去

目录结构

avatar.png是头像,你们随便用啥。

UserController

@RestController
public class UserController {

    @GetMapping(value = "/getUser/{id}")
    public User getUser(@PathVariable("id") Long id) {
        // id没用上,就是演示一下@PathVariable注解
        System.out.println("id:" + id);

        User user = new User();
        user.setName("mx");
        user.setAge(18);
        user.setAddress("wenzhou");

        return user;
    }
}

index.htm

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS</title>
    <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>
</head>
<body>
<h1>当前网页来自localhost:7070/index.html</h1>

<h3>页面加载时自动发送GET请求: http://localhost:8080/avatar.png</h3>
<img src="http://localhost:8080/avatar.png" width="100" height="100"><br><br>

<h3>点击发送GET请求: http://localhost:8080/getUser/1</h3>
<input type="text" id="result">
<input type="button" onclick="onButtonClick()" value="get_button">

</body>

<script>
    function onButtonClick() {
            $.get('http://localhost:8080/getUser/1', function (data) {
                console.log("data", data);
            });
    }
</script>

</html>

JQuery你们可以引用外站的:

<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>

刚才提到过,协议、端口、域名都能造成跨域,这里我们演示最简单的跨域:端口不同导致跨域,我会把同一个项目启动两次。

第一次通过IDEA启动7070端口:

 

第二次通过java -jar指定8080端口启动应用(或者可以再配置一个IDEA的启动按钮,更改yml端口后启动即可):

# install
mvn clean install -Dmaven.test.skip=true
# 指定8080端口启动项目
java -jar /Users/kevin/IdeaProjects/springboot-demo/target/springboot-demo-0.0.1-SNAPSHOT.jar --server.port=8080

请求端口为7070的应用,得到index.html页面,随后点击按钮,向端口为8080的应用发起请求:

index.html来自端口为7070的应用,它总共向端口为8080的应用发送两次请求:

  • <img>标签自动发起请求,获取头像成功
  • 点击botton发起请求,获取User失败

但botton的GET请求真的失败了吗?其实AJAX已经拿到了数据(状态码200),只不过浏览器拒绝该数据。

回顾一下开头的那张示意图:

本案例中,页面index.html来自localhost:7070,而<img>和AJAX的GET都指向localhost:8080,都跨域了:

  • <img src="http://localhost:8080/avatar.png"/>
  • $.get('http://localhost:8080/getUser/1', function (data){...})

但为什么只有AJAX被拒绝了?

因为浏览器允许<img>跨域。

是的,浏览器遵守同源策略,但是有若干个标签是允许跨域的,比如:

  • <img src="xxx"/>
  • <link href="xxx"/>
  • <script src="xxx"/>

我之前说Jquery脚本可以直接引用外站的,就是这个道理,因为<script>也不会跨域(所以即使之前不知道什么是跨域,各种引用也没报错)。

现在,我们主要关心如何解决AJAX跨域问题。

解决跨域

解决跨域的方式很多,比如Node中间件代理、Nginx反向代理等等。这里介绍两种最简单的方式:JSONP和CORS。

JSONP

JSONP可以算是利用同源策略的“漏洞”而创造出来的跨域解决方案,属于奇巧淫技,主要是利用<script>标签实现的(因为<script>请求回来的数据不会被拦截)。JQuery的JSONP在用法上很简单,为了能让部分同学更好地理解它,我们绕一个弯子,通过几次版本迭代最终引出JSONP。

在此之前,有两个前置知识要说明一下。

首先,<script>虽然可以避免跨域问题,但它的内容一般都是JS脚本,要么直接在<script>标签中编写JS脚本,要么网络请求JS脚本(文件)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS</title>
    <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>
    <!--1.定义printResponse()方法-->
    <script>
        function printResponse(data) {
            console.log(data);
        }
    </script>
    
    <!--使用script调用-->
    
    <!--2.1 本地编写JS脚本-->
    <script>
        printResponse({
            "name": "bravo",
            "age": 18,
            "address": "Hangzhou"
        })
    </script>
    <!--2.2 网络请求JS脚本-->
    <script type="text/javascript" src="http://localhost:8080/getUser/1"></script>
    
</head>
<body>
</body>
</html>

其次,<script>的引入顺序要讲究,因为浏览器是从上到下加载的。


好了,让我们正式开始。

第一版:返回JS脚本

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS</title>
    <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>
    
    <!--1.定义printResponse()方法-->
    <script>
        function printResponse(data) {
            console.log(data);
        }
    </script>

    <!--2 网络请求JS脚本-->
    <script type="text/javascript" src="http://localhost:8080/getUser/1"></script>

</head>
<body>
<h1>当前网页来自localhost:7070/index.html</h1>

<h3>页面加载时自动发送GET请求: http://localhost:8080/avatar.png</h3>
<img src="http://localhost:8080/avatar.png" width="100" height="100"><br>


</body>

</html>

Controller

@GetMapping(value = "/getUser/{id}")
public String getUser(@PathVariable("id") String id) {

    System.out.println("id:" + id);

    return "printResponse" + "(" + "{\n" +
            "  \"username\": \"mx\",\n" +
            "  \"age\": 18,\n" +
            "  \"address\": \"China\"\n" +
            "}" + ")";

}

既然<script>标签一般请求的是JS脚本,我们干脆直接返回一段脚本。

这里可有讲究啊!原本是:

  • 前端定义方法、前端调用方法处理数据 + 后端返回数据

现在是:

  • 前端定义方法 + 后端返回脚本(调用方法+返回数据)

也就是说,原本属于前端的一部分工作被挪到后端了(前后端又不分离了…)。

不过暂且不管这些,先来验证一下Controller是可用的(浏览器直接请求):

返回了整个字符串。

请求index.html

完美。

整个过程是:

  • 把本应该直接写在<script>标签里的代码片段挪到localhost:8080的Controller中
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS</title>
    <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>
    <!--1.定义printResponse()方法-->
    <script>
        function printResponse(data) {
            console.log(data);
        }
    </script>
    
    <!--使用script调用-->
    
    <!-- 把这个片段挪到localhost:8080的Controller中
    <script>
        printResponse({
            "name": "bravo",
            "age": 18,
            "address": "Hangzhou"
        })
    </script>
		-->
    <!--通过网络请求JS脚本-->
    <script type="text/javascript" src="http://localhost:8080/getUser/1"></script>
    
</head>
<body>
</body>
</html>
  • 利用<script>的跨域特性,请求并接收(脚本+数据),最终浏览器执行这个脚本完成需求

第二版:callback动态方法名

仔细想想,第一个版本有个最大的问题是:可扩展性差。

原本AJAX请求返回的只是数据,然后前端自己决定调用function A或function B处理,想怎么处理怎么处理。但现在使用<script>后,由于返回的是脚本,要提前指定脚本里写什么,也就是提前指定本次处理数据的是function A还是function B。

比如,前端希望得到User数据后用function dealUser()处理,那么他需要和后端提前沟通,让后端把脚本里的方法名写死:dealUser。

@RestController
public class UserController {

    @GetMapping(value = "/getUser/{id}")
    public String getUser(@PathVariable("id") String id) {
        System.out.println("id:" + id);

        return "dealUser" + "(" + "{\n" +
                "  \"username\": \"mx\",\n" +
                "  \"age\": 18,\n" +
                "  \"address\": \"China\"\n" +
                "}" + ")";
    }
}

如果前端临时改变主意,得到User后希望调用printUser()处理,又要通知后端改脚本...(这还能叫前后端分离吗?)

但是仔细分析一下,后端要改的其实就是一个方法名,不要让后端写死,前端请求时作为参数带上不就好了吗?!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS</title>
    <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>

    <!--定义printResponse()方法-->
<!--    <script>-->
<!--        function printResponse(data) {-->
<!--            console.log(data);-->
<!--        }-->
<!--    </script>-->

    <!--新定义的doSomething()方法-->
    <script>
        function doSomething(data) {
            console.log(data);
        }
    </script>

    <!--2 网络请求JS脚本,带上方法名-->
    <script type="text/javascript" src="http://localhost:8080/getUser/1?callback=doSomething"></script>

</head>
<body>
<h1>当前网页来自localhost:7070/index.html</h1>

<h3>页面加载时自动发送GET请求: http://localhost:8080/avatar.png</h3>
<img src="http://localhost:8080/avatar.png" width="100" height="100"><br>


</body>

</html>

@RestController
public class UserController {

    @GetMapping(value = "/getUser/{id}")
    public String getUser(@PathVariable("id") String id, String callback) {

        System.out.println("id:" + id);
        // 拼接方法名
        return callback + "(" + "{\n" +
                "  \"username\": \"mx\",\n" +
                "  \"age\": 18,\n" +
                "  \"address\": \"China\"\n" +
                "}" + ")";

    }
}

第三版:JQuery的JSONP

经过前两版的演变,大家其实已经知道JSONP的大致原理了,来看看JQuery的JSONP处理方式是怎样的吧~

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS</title>
    <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>
</head>
<body>
<h1>当前网页来自localhost:7070/index.html</h1>

<h3>页面加载时自动发送GET请求: http://localhost:8080/avatar.png</h3>
<img src="http://localhost:8080/avatar.png" width="100" height="100"><br><br>

<h3>点击发送GET请求: http://localhost:8080/getUser/1</h3>
<input type="text" id="result">
<input type="button" onclick="onButtonClick()" value="get_button">

</body>

<script>
    /**
     * 终于不用和<script>打交道了,我们还是照常使用ajax,只要把dataType改为"jsonp"即可。
     * 整个流程是:
     * 1.前端:在发送请求前,底层会为URL拼接方法名,方法名是随机的,比如callback=jQuery132414...
     * 2.后端:得到callback,拼接脚本和数据并返回
     * 3.前端:解析从后端得到的js片段,执行jQuery132414...,最终会把正确的值传入success:function()
     */
    function onButtonClick() {
        $.ajax({
            type: "GET",
            url: "http://localhost:8080/getUser/1",
            dataType: "jsonp",
            success: function(data){
                $('#result').val(data.name);
            }
        });
    }
</script>

</html>
@RestController
public class UserController {

    /**
     * SpringBoot内置的Jackson
     */
    @Autowired
    private ObjectMapper objectMapper;


    @GetMapping(value = "/getUser/{id}")
    public String getUser(@PathVariable("id") String id, String callback) {

        System.out.println("id:" + id);

        User user = new User();
        user.setName("mx");
        user.setAge(18);
        user.setAddress("China");

        // 拼接方法名
        try {
            return callback + "(" + objectMapper.writeValueAsString(user) +")";
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        return null;

    }
}

可以看到,JQuery自动帮我们在URL里加了callback参数。复制URL直接用浏览器请求:

大致流程是:前端随机生成不可见的方法xxx -> 传递到后端,后端拼接JS脚本xxx(User) -> 前端得到JS脚本调用xxx(User) -> 最终调用到success:function(data){...}

随机生成的xxx方法其实就是一个代理而已。

CORS

JSONP的缺点其实挺多的,百度一下就能了解到。最显著的缺点其实是JSONP只支持GET请求,毕竟它的灵感来自于<script>标签,而标签都是GET请求。

从这个角度说,CORS要比JSONP强大且灵活。

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

CORS需要浏览器和服务器同时支持。在JSONP中,前端带来了callback方法名,需要后端配合返回JS片段。而在CORS中,浏览器会自动带来一些请求头,后端需要针对这些请求头做一些处理。

简单地说:JSONP是前端代码层面带一些东西给后端,CORS则是浏览器层面带一些东西给后端。本质上都需要前后端协商。

不过目前所有浏览器都支持该功能(会自动带上需要的请求头),IE浏览器不能低于IE10。所以最终来看,CORS这种方案不需要前端做任何事情(绝大部分浏览器支持),只需后端配合即可。

对于服务端而言:

CORS通信与AJAX没有任何差别,因此我们不需要改变以前的业务逻辑。只不过,浏览器会在请求中携带一些头信息,我们需要以此判断是否允许其跨域,然后在响应头中加入一些信息即可。这一般通过过滤器完成即可。

CORS简略示意图(简单请求):

方法上加@CrossOrigin

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS</title>
    <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>
</head>
<body>
<h1>当前网页来自localhost:7070/index.html</h1>

<h3>页面加载时自动发送GET请求: http://localhost:8080/avatar.png</h3>
<img src="http://localhost:8080/avatar.png" width="100" height="100"><br><br>

<h3>点击发送GET请求: http://localhost:8080/getUser/1</h3>
<input type="text" id="result">
<input type="button" onclick="onButtonClick()" value="get_button">

</body>

<script>
    /**
     * 普通AJAX,没有设置jsonp
     */
    function onButtonClick() {
        $.ajax({
            type: "GET",
            url: "http://localhost:8080/getUser/1",
            success: function(data){
                $('#result').val(data.name);
            }
        });
    }
</script>

</html>
/**
 * @author mx
 */
@RestController
public class UserController {

    /**
     * 在跨域方法上加@CrossOrigin即可完美解决跨域问题
     * @param id
     * @return
     */
    @CrossOrigin("http://localhost:7070")
    @GetMapping(value = "/getUser/{id}")
    public User getUser(@PathVariable("id") String id) {

        System.out.println("id:" + id);

        User user = new User();
        user.setName("bravo");
        user.setAge(18);
        user.setAddress("China");

        return user;

    }
}

Controller上加@CrossOrigin

@CrossOrigin还可以加载Controller上,这样Controller的所有方法都支持跨域。

@RestController
@CrossOrigin("http://localhost:7070")
public class UserController {

    @GetMapping(value = "/getUser/{id}")
    public User getUser(@PathVariable("id") String id) {

        System.out.println("id:" + id);

        User user = new User();
        user.setName("bravo");
        user.setAge(18);
        user.setAddress("China");

        return user;

    }
}

@Bean配置跨域Filter

/**
 * @author mx
 */
@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        //1.添加CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        //1) 允许的域,不要写*,否则cookie就无法使用了
        config.addAllowedOrigin("http://localhost:7070");
        //2) 是否发送Cookie信息
        config.setAllowCredentials(true);
        //3) 允许的请求方式
        config.addAllowedMethod("*");
        // 4)允许的头信息
        config.addAllowedHeader("*");
        // 5) 有效时长
        config.setMaxAge(3600L);

        //2.添加映射路径,我们拦截一切请求
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**", config);

        //3.返回新的CorsFilter.
        return new CorsFilter(configSource);
    }
}

WebMvcConfigurer添加跨域规则

/**
 * @author mx
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:7070")
                .allowCredentials(true)
                .allowedHeaders("*")
                .allowedMethods("*")
                .maxAge(3600L);
    }
}

以上三种方式任选一种皆可,推荐最后两种。你会发现,在上面几种方式的演进中,跨域规则被一步步抽取到“更高层次”的位置。如果你们公司恰好使用微服务,还可以把跨域规则提取到网关,也就是把Filter移到网关对所有微服务做统一跨域处理。

CORS原理

回顾之前跨域的报错信息:

浏览器认为只要后端没返回CORS头(Access-Control-Allow-Origin),就认为后端不允许跨域,返回的数据不可靠。

所以只要后端能够返回浏览器需要的请求头,即可跨域(响应数据就不会被同源策略抛弃):

上面是表面原理,底层原理比较复杂。

浏览器会将ajax请求分为两类,其处理方案略有差异:简单请求、特殊请求。

简单请求

只要同时满足以下两大条件,就属于简单请求。:

(1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

当浏览器发现发起的ajax请求是简单请求时,会在请求头中携带一个字段:Origin.

刚才说过,CORS需要客户端和服务端同时支持。上面这个小操作,算是客户端的支持行为(IE10以下不行)。

Origin中会指出当前请求属于哪个域(协议+域名+端口)。服务会根据这个值决定是否允许其跨域。

如果服务器允许跨域,需要在返回的响应头中携带下面信息(算是服务端的支持):

Access-Control-Allow-Origin: http://manage.leyou.com
Access-Control-Allow-Credentials: true
Content-Type: text/html; charset=utf-8

  • Access-Control-Allow-Origin:可接受的域,是一个具体域名或者*(代表任意域名)
  • Access-Control-Allow-Credentials:是否允许携带cookie,默认情况下,cors不会携带cookie,除非这个值是true

有关cookie:

要想操作cookie,需要满足3个条件:

  • 服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。
  • 浏览器发起ajax需要指定withCredentials 为true
  • 响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名

这样一来,前后端都支持跨域了,那就跨吧。

特殊请求

不符合简单请求的条件,会被浏览器判定为特殊请求,,例如请求方式为PUT。

预检请求

特殊请求会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

一个“预检”请求的样板:

OPTIONS /cors HTTP/1.1
Origin: http://manage.leyou.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.leyou.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

与简单请求相比,除了Origin以外,多了两个头:

  • Access-Control-Request-Method:接下来会用到的请求方式,比如PUT
  • Access-Control-Request-Headers:会额外用到的头信息

预检请求的响应

服务的收到预检请求,如果许可跨域,会发出响应:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://manage.leyou.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Max-Age: 1728000
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

除了Access-Control-Allow-OriginAccess-Control-Allow-Credentials以外,这里又额外多出3个头:

  • Access-Control-Allow-Methods:允许访问的方式
  • Access-Control-Allow-Headers:允许携带的头
  • Access-Control-Max-Age:本次许可的有效时长,单位是秒,过期之前的ajax请求就无需再次进行预检了

如果浏览器得到上述响应,则认定为可以跨域,后续就跟简单请求的处理是一样的了。

另外,需要注意CSRF攻击,也就是跨域伪造请求。即使有同源策略存在,如果不做另外防护,此类攻击仍然奏效。具体大家另外了解。

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

进群,大家一起学习,一起进步,一起对抗互联网寒冬

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/217870.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JUC】二十、volatile变量的特点与使用场景

文章目录 1、volatile可见性案例2、线程工作内存与主内存之间的原子操作3、volatile变量不具有原子性案例4、无原子性的原因分析&#xff1a;i5、volatile变量小总结6、重排序7、volatile变量禁重排的案例8、日常使用场景9、总结 volatile变量的特点&#xff1a; 可见性禁重排无…

如何利用企业软件著作权查询API提升知识产权管理效率

引言 在当今数字化时代&#xff0c;企业的知识产权管理变得愈发重要。其中&#xff0c;软件著作权作为企业重要的知识产权之一&#xff0c;其保护和管理对于企业的创新和竞争力至关重要。为了更高效地进行软件著作权管理&#xff0c;许多企业开始采用先进的技术手段&#xff0…

Chrome清除特定网站的Cookie,从而让网址能正常运行(例如GPT)

Chrome在使用某些网址的时候&#xff0c;例如GPT的时候&#xff0c;可能会出现无法访问这个网址的情况&#xff0c;就是点不动啥的 只需要把你需要重置的网址删除就好了

Leetcode题库(数据库合集)_ 难度:中等

目录 难度&#xff1a;中等1.股票的资本损益2. 当选者3. 页面推荐4. 2016年的投资5. 买下所有产品的人6. 电影评分6. 确认率7. 按分类统计薪水8. 餐馆营业额的变化增长8. 即时食物配送 ①9. 至少有5名直系下属的经理10. 游戏玩法分析11. 好友申请&#xff1a;谁有最多的好友12.…

TypeScript中的类

TypeScript 类 1.TypeScript中类的意义 ​ 相对以前 JavaScript 不得不用 构造函数来充当”类“&#xff0c;TypeScript 类的出现可以说是一次技术革命。让开发出来的项目尤其是大中项目的可读性好&#xff0c;可扩展性好了不是一点半点。 ​ TypeScrip 类的出现完全改变了前…

React创建项目

React创建项目 提前安装好nodejs再进行下面的操作&#xff0c;通过node -v验证是否安装 1.设置源地址 npm config set registry https://registry.npmmirror.com/2.确认源地址 npm config get registry返回如下 https://registry.npmmirror.com/3.输入命令 npx create-re…

ruby安装(vscode、rubymine)

https://rubyinstaller.org/downloads/ 下载exe安装即可 会弹出 输入3 安装成功 vscode插件市场安装ruby插件 新建一个目录&#xff0c;打开terminal bundle init //进行初始化&#xff08;如果执行不了&#xff0c;应该是环境变量没生效&#xff0c;重启vscode&#…

企业架构LB-服务器的负载均衡之LVS实现

企业架构LB-服务器的负载均衡之LVS实现 学习目标和内容 1、能够了解LVS的基本工作方式 2、能够安装配置LVS实现负载均衡 3、能够了解LVS-NAT的配置方式 4、能够了解LVS-DR的配置方式 #一、LVS介绍和安装 LVS&#xff08;Linux Virtual Server&#xff09;即Linux虚拟服务器&…

AWS攻略——VPC初识

大纲 在网络里启动一台可以ssh上去的机器查看区域、VPC和子网创建EC2连接Web端连接客户端连接 知识点参考资料 VPC是在AWS架构服务的基础&#xff0c;有点类似于我们在机房里拉网线和设置路由器等。等这些设施完备后&#xff0c;我们才能考虑给机器部署服务。而很多初识AWS的同…

【OpenGL】窗口的创建

从今天开始我们开始学习OpenGL&#xff0c;从0开始&#xff0c;当然是有C基础的前提 首先包含glad和GLFW的头文件 #include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> 初始化 GLFW 在 main 函数中&#xff0c;我们首先使用 glfwInit 初…

西南科技大学模拟电子技术实验六(BJT电压串联负反馈放大电路)预习报告

一、计算/设计过程 BJT电压串联负反馈放大电路图1-1-1-1为BJT电压串联负反馈放大实验电路,若需稳定输出电压,减小从信号源所取电流,可引入电压串联负反馈闭合开关。 图1-1-1-1 理论算法公式(1)闭环电压放大倍数 (2)反馈系数 (3)输入电阻 (4)输出电阻 计算过程。开环…

Latex公式中矩阵的方括号和圆括号表示方法

一、背景 在使用Latex写论文时&#xff0c;不可避免的涉及到矩阵公式。有的期刊要求矩阵用方括号&#xff0c;有的期刊要求矩阵用圆括号。因此&#xff0c;特记录一下Latex源码在两种表示方法上的区别&#xff0c;以及数组和方程组的扩展。 二、矩阵的方括号表示 首先所有的…

二值图像分割统一项目

1. 项目文件介绍 本章为二值图像的分割任务做统一实现&#xff0c;下面是项目的实现目录 项目和文章绑定了&#xff0c;之前没用过&#xff0c;不知道行不行 data 文件夹下负责摆放数据的训练集测试集inference 负责放待推理的图片(支持多张图片预测分割)run_results 是网络训…

实体、协议、服务和服务访问点

目录 一、概念 二、相邻两层之间的关系 三、面向连接服务的特点 四、无连接服务的特点 五、著名的协议举例 一、概念 实体&#xff08;entity&#xff09;表示任何可发送或接收信息的硬件或软件进程。同机器上同一层的实体叫做对等实体&#xff08;peer entity&#xff0…

【算法专题】前缀和

前缀和 前缀和1. 前缀和【模板】2. 二维前缀和【模板】3. 寻找数组的中心下标4. 除自身以外数组的乘积5. 和为K的子数组6. 和可被K整除的子数组7. 连续数组8. 矩阵区域和 前缀和 1. 前缀和【模板】 题目链接 -> Nowcoder -DP34.前缀和【模板】 Nowcoder -DP34.前缀和【模…

计算机网络:传输层——多路复用与解复用

文章目录 前言一、Socket&#xff08;套接字&#xff09;二、多路复用/解复用三、多路解复用&#xff08;1&#xff09;多路解复用原理&#xff08;2&#xff09;无连接&#xff08;UDP&#xff09;多路解复用&#xff08;3&#xff09;面向连接&#xff08;TCP&#xff09;的多…

15:00的面试,15:06就出来了,问的问题过于变态了。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到5月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

neuq-acm预备队训练week 8 B3647 【模板】Floyd 题解

题目描述 给出一张由 n 个点 m 条边组成的无向图。 求出所有点对(i,j) 之间的最短路径。 题目限制 输入格式 第一行为两个整数 n,m&#xff0c;分别代表点的个数和边的条数。 接下来 m 行&#xff0c;每行三个整数u,v,w&#xff0c;代表 u,v 之间存在一条边权为 w 的边。 …

pip的基本命令和使用

pip 简介 pip是Python官方的包管理器&#xff0c;可以方便地安装、升级和卸载Python包。 pip 常用命令 显示版本和路径 pip --version获取帮助 pip --help升级pip和升级包 pip install --upgrade pip # Linux/macOS pip install -U pip # windowspip install…

基于SSM的图书馆管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…