同源策略与跨域

作者简介:大家好,我是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*/@Autowiredprivate 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 {@Beanpublic 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 {@Overridepublic 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

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

千锋 Vue 详细笔记整理

视频笔记是根据B站 千锋 涛哥 - SpringBootvue前后端分离项目《锋迷商城》实战课-完结版 进行整理的 笔记可上 gitee仓库 自取 千锋 Vue 笔记整理 一、vue 的简介1.1 使用 JQuery 的复杂性问题1.2 VUE 简介1.2.1 前端框架1.2.2 MVVM 二、 vue 入门使用2.1 vue 的引入2.2 入门案…

期末速成数据库极简版【存储过程】(5)

目录 【7】系统存储过程 【8】用户存储过程——带输出参数的存储过程 创建存储过程 存储过程调用 【9】用户存储过程——不带输出参数的存储过程 【7】系统存储过程 系统存储我们就不做过程讲解用户存储过程会考察一道大题&#xff0c;所以我们把重点放在用户存储过程。…

Navicat 与 华为云 GaussDB 合作再升级,赋能 GaussDB 分布式数据库

2023 年第三季度&#xff0c;Navicat 首次支持了华为云 GaussDB 主备版数据库。经过双方团队进一步的深化合作&#xff0c;Navicat 完成了 GaussDB 分布式的研发适配工作&#xff0c;赋能 GaussDB 全域数据库产品。 GaussDB 数据库分为主备版和分布式版两种模式。主备版适用于…

【Backbone】TransNeXt:最新ViT模型(原理+常用神经网络汇总)

文章目录 一、近几年神经网络 Backbone 回顾1.Densenet 与 Resnet2.CBP3.SENet4.GCNet5.DANet6.PANet 与 FPN7.ASPP8.SPP-net9.PSP-net10.ECA-Net 二、TransNeXt&#xff08;2023&#xff09;1.提出问题2.Aggregated Pixel-focused Attention2.1 Pixel-focused Attention&#…

matlab RGB三元组和十六进制的转换

matlab画柱状图改颜色的时候&#xff0c;用三元组的形式&#xff0c;范围是[0&#xff0c;1] 我们获得了十六进制 到网站转换为[0,255] https://c.runoob.com/front-end/55/ 然后将得到的值/255 输入matlab就可以了

idea__SpringBoot微服务05——JSR303校验(新注解)(新的依赖),配置文件优先级,多环境切换

JSR303校验&#xff0c;配置文件优先级&#xff0c;多环境切换 一、JSR303数据校验二、配置文件优先级三、多环境切换一、properties多环境切换二、yaml多环境切换————————创作不易&#xff0c;如觉不错&#xff0c;随手点赞&#xff0c;关注&#xff0c;收藏(*&#x…

DOS命令

1.cd.. 返回主目录 2.cd 目录 切换到当前目录 3.dir 查看目录的所有文件夹 4.cls 清除dos窗口所有内容 5.键盘的向上箭头 查看上面输入的命令 6.exit退出dos窗口

国产化软件突围!怿星科技eStation产品荣获2023铃轩奖“前瞻优秀奖”

11月11日&#xff0c;2023中国汽车供应链峰会暨第八届铃轩奖颁奖典礼在江苏省昆山市举行。怿星科技凭借eStation产品&#xff0c;荣获2023铃轩奖“前瞻智能座舱类优秀奖”&#xff0c;怿星CEO潘凯受邀出席铃轩奖晚会并代表领奖。 2023铃轩奖“前瞻智能座舱类优秀奖” 铃轩奖&a…

JAVA实现敏感词高亮或打码过滤:sensitive-word

练手项目中实现发表文章时检测文章是否带有敏感词&#xff0c;以及对所有敏感词的一键过滤功能 文章目录 效果预览实现步骤 效果预览 随便复制一篇内容到输入框 机器审核文章存在敏感词&#xff0c;弹消息提示并进入人工审核阶段&#xff08;若机器审核通过&#xff0c;则无需审…

树莓派 5 - Raspberry Pi 5 入门教程

系列文章目录 文章目录 ​​​​​​​ 前言 如果您是第一次使用 Raspberry Pi&#xff0c;请参阅我们的入门指南&#xff08;how to get started&#xff09;。 Raspberry Pi 5 Raspberry Pi 5 配备了运行频率为 2.4GHz 的 64 位四核 Arm Cortex-A76 处理器&#xff0c;CPU 性…

【Selenium+Webmagic】基于JAVA语言实现爬取js渲染后的页面,附有代码

事先声明 笔者最近需要查看一些数据&#xff0c;自己挨个找太麻烦了&#xff0c;于是简单的学了一下爬虫。笔者在这里声明&#xff0c;爬的数据只为学术用&#xff0c;没有其他用途&#xff0c;希望来这篇文章学习的同学能抱有同样的目的。 枪本身不坏&#xff0c;坏的是使用枪…

【漏洞复现】万户协同办公平台ezoffice wpsservlet接口存在任意文件上传漏洞 附POC

漏洞描述 万户ezOFFICE集团版协同平台以工作流程、知识管理、沟通交流和辅助办公四大核心应用 万户ezOFFICE协同管理平台是一个综合信息基础应用平台。 万户协同办公平台ezoffice wpsservlet接口存在任意文件上传漏洞。 免责声明 技术文章仅供参考,任何个人和组织使用网络应…

搭乘“低代码”快车,引领食品行业数字化转型全新升级

数字化技术作为重塑传统行业重要的力量&#xff0c;正以不可逆转的趋势改变着企业经营与客户消费的方式。 在近些年的企业数字化服务与交流过程中&#xff0c;织信团队切实感受到大多数企业经营者们从怀疑到犹豫再到焦虑最终转为坚定的态度转变。 在这场数字化转型的竞赛中&a…

如何将腾讯混元大模型AI接入自己的项目里(中国版本ChatGPT)

如何将腾讯混元大模型AI接入自己的项目里 一、腾讯混元大模型API二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、腾讯混元大模型API 基于腾讯混元大模型AI的智能文本对话AI机器人…

Kafka安全性探究:构建可信赖的分布式消息系统

在本文中&#xff0c;将研究Kafka的安全性&#xff0c;探讨如何确保数据在传输和存储过程中的完整性、机密性以及授权访问。通过详实的示例代码&#xff0c;全面讨论Kafka安全性的各个方面&#xff0c;从加密通信到访问控制&#xff0c;帮助大家构建一个可信赖的分布式消息系统…

uniapp开发小程序经验记录

uniapp开发小程序的过程中会遇到很多问题&#xff0c;这里记录一下相关工具优化&#xff0c;便于后来者参考。 每次保存代码后&#xff0c;小程序都跳回首页 针对这个问题&#xff0c;常规的做法就是修改pages配置文件&#xff0c;但是这种方式不便于路由参数的设置&#xff…

汽车电子智能保险丝解决方案

一、背景知识 在过去的几十年里&#xff0c;电子在汽车系统创新中发挥了关键作用。新型半导体器件具有新颖的功能&#xff0c;增强了车辆机械系统提供的功能。 虽然半导体解决方案和电子产品将继续在汽车电子产品中发挥关键作用&#xff0c;但展望未来&#xff0c;汽车创新将…

Ubuntu环境下使用GDB调试C语言项目

1. 安装gdb //终端输入 sudo apt-get install gdb 2. 启动gdb gdb GDB常用命令大全&#xff0c;参考此篇博客 使用GDB调试C项目中的makefile 1.在内核配置中启用调试信息&#xff1a; 在内核配置中&#xff0c;确保启用了调试信息。可以通过以下步骤来配置内核&#xff1…

python-04(入门基础篇4——lists相关的部分语法)

python-04&#xff08;入门基础篇4——lists相关的部分语法&#xff09; 1. 前言1.1 python入门1.2 参考官网 2. 关于索引和切片3. 在列表追加元素3.1 支持拼接3.2 使用list.append() 方法在列表末尾添加新项 4. 列表是可变类型4.1 更改其中某元素内容4.2 使用切片更改列表大小…

Android 等待view 加载布局完成 (包括动态生成View)

前言 在实际开发中&#xff0c;有很多组件需要 根据数据&#xff0c;动态生成&#xff0c;或者 追加 / 减少 子view&#xff0c;由于View布局需要时间&#xff0c;此时想要获取父View的最新宽高值&#xff0c;要么手动测量&#xff0c;要么等待布局完成后再获取&#xff1b; …