CORS跨域资源共享
-
CORS
-
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
。 -
它允许浏览器向跨源服务器,发出XMLHttpRequest(ajax)请求,从而克服了AJAX只能
同源使用的限制
。
-
-
同源策略
同源策略
[same origin policy]是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。 同源策略是浏览器安全的基石。
-
源
源
[origin]就是协议、域名和端口号。例如:http://www.baidu.com:80这个URL。
-
同源
- 若地址里面的
协议、域名和端口号均相同
则属于同源。
# 同源举例 - 例如判断下面的URL是否与 http://www.a.com/test/index.html 同源 http://www.a.com/dir/page.html --------->同源 http://www.child.a.com/test/index.html ->不同源,域名不相同 https://www.a.com/test/index.html ------>不同源,协议不相同 http://www.a.com:8080/test/index.html -->不同源,端口号不相同
- 若地址里面的
哪些操作不受同源限制
-
页面中的链接,重定向以及表单提交是不会受到同源策略限制的;
-
跨域资源的引入是可以的。如嵌入到页面中的
<script src="..."></script>
,<img>
,<link>
,<iframe>
等。
哪些操作受到同源限制
- 在浏览器中发起一个AJAX请求,会受到同源策略限制。
使用CORS解决同源限制
-
@CrossOrigin注解(在需要跨域访问的controller上加上@CrossOrigin则该controller就可以进行跨域访问了)
// 局部解决跨域问题 @RestController @RequestMapping("demos") @CrossOrigin public class DemoController { @GetMapping public String demos() { System.out.println("========demo======="); return "demo ok"; } }
-
全局解决跨域问题
package com.baizhi.config; 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 CorsConfig { @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); // 1允许任何域名使用 corsConfiguration.addAllowedHeader("*"); // 2允许任何头 corsConfiguration.addAllowedMethod("*"); // 3允许任何方法(post、get等) source.registerCorsConfiguration("/**", corsConfiguration);//4处理所有请求的跨域配置 return new CorsFilter(source); } }
在进行前后端分离开发时,前端部署在一台服务器上,这台服务器有它自己的域名,有它自己的ip,后端系统也有自己的域名,也有自己的ip,前端和后端属于不同的源,而前端和后端是通过ajax进行通信的,ajax不能在不同源之间通信,为了解决ajax不能跨域访问,就有了CORS的产生,CORS是专门解决ajax不能跨域访问的问题的。
接下来我们来进行测试
用到的包结构
- 因为要用到themeleaf,所以需要导入themeleaf依赖并在application.yml中进行配置
我们使用themeleaf,所以要导入themeleaf依赖,并且在配置文件中配置一下themeleaf的模板目录和模板后缀
<!--使用thymelaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
application.yml
spring:
thymeleaf:
prefix: classpath:/templates/ # 指定thymeleaf模板前缀目录
suffix: .html # 指定模板的后缀
cache: false # 是否开启thymeleaf缓存,默认为true是开启的,在开发过程中建议 # 关了
- 创建cors.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function test(){
// 1. 创建xhr对象
var xhr = new XMLHttpRequest();
// 1.5 处理结果
xhr.onreadystatechange = function (){
if(xhr.status == 200 && xhr.readyState == 4){
console.log(xhr.responseText);
document.getElementById("msg").innerText = "返回的结果为: " + xhr.responseText;
}
}
// 2. 发送请求
xhr.open("GET", "http://localhost:8080/demos"); // 访问本地服务器
xhr.send();
}
</script>
</head>
<body>
<h1>cors 跨域测试</h1>
<input type="button" value="点我发送ajax请求" onclick="test()">
<h4 id="msg"></h4>
</body>
</html>
- 创建controller
@RestController
@RequestMapping("demos")
public class DemoController {
@GetMapping
public ResponseEntity<String> demo(){
System.out.println("demo ok");
return new ResponseEntity<>("demook", HttpStatus.OK);
}
@GetMapping("/{id}")
public ResponseEntity<String> demo1(@PathVariable("id") Integer id){
System.out.println("demo ok " + id);
if(id < 0) throw new IllegalNumberException("无效id,请检查!");
return new ResponseEntity<>("demo ok", HttpStatus.OK);
}
}
- 进行跨域访问
- 解决方案
-
方案一:局部跨域解决方案(在controller上加上@CrossOrigin注解)
-
方案二:全局跨域解决方案(创建一个配置类,并在内部注册CorsFilter对象)
package com.baizhi.config;
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 CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1允许任何域名使用
corsConfiguration.addAllowedHeader("*"); // 2允许任何头
corsConfiguration.addAllowedMethod("*"); // 3允许任何方法(post、get等)
source.registerCorsConfiguration("/**", corsConfiguration);//4处理所有请求的跨域配置
return new CorsFilter(source);
}
}
在使用全局解决方案时导包时需要注意不要导错包啦
注:全局解决方案和局部解决方案使用一个即可,两个都使用又会产生新的问题
- 使用解决方案之后
Jasypt加密
引言
Jasypt 也即Java Simplified Encryption是Sourceforge.net上的一个开源项目。在当地时间11月23号的通告中,Jasypt 1.4的新特征包括:加密属性文件(encryptable properties files)、Spring Framework集成、加密Hibernate数据源配置、新的命令行工具、URL加密的Apache wicket集成以及升级文档。
根据Jasypt文档,该技术可用于加密任务与应用程序,例如加密密码、敏感信息和数据通信、创建完整检查数据的sums. 其他性能包括高安全性、基于标准的加密技术、可同时单向和双向加密的加密密码、文本、数字和二进制文件。Jasypt也可以与Acegi Security整合也即Spring Security。Jasypt亦拥有加密应用配置的集成功能,而且提供一个开放的API从而任何一个Java Cryptography Extension都可以使用Jasypt。
Jasypt还符合RSA标准的基于密码的加密,并提供了无配置加密工具以及新的、高可配置标准的加密工具。
Jasypt加密时需要交给其一个秘钥,然后配置一下秘钥,之后将需要加密的字符串交给stringEncryptor类的方法进行加密,根据加密之后得到的加密字符串和秘钥还可以由stringEncryptor方法进行解密,解密之后的字符串就是我们加密之后的字符串对应的原字符串,所以这个秘钥是很重要的,如果让别人知道了这个秘钥,那么Jasypt加密就没有任何的意义了,这个秘钥可以是任何字符串,我们一定不能让其他人知道这个秘钥。
另外,使用Jasypt对于同一个字符串和相同的秘钥进行多次加密得到的字符串不同,但是使用相同的秘钥和这些不同的字符串进行解密得到的原串是一样的,也就是说,我们可以取这些加密之后的字符串中的任意一个作为加密之后的串,无论如果,解密之后的串一定是原串。
使用Jasypt进行加密的步骤:
-
引入依赖
<!--jasypt加密--> <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency>
-
之后利用stringEncryptor类的方法对
我们在实际开发中演示一下如何使用Jasypt进行加密
用到的包结构
- 先在JVM参数中配置秘钥,stringEncryptor类的方法对需要加密的字符串进行加密操作,得到加密串
配置秘钥
秘钥可以是任意字符串,我们将秘钥设置为 cf150b74e4824146ad76e9ebe757ba76
之后设置JVM参数
-Djasypt.encryptor.password=cf150b74e4824146ad76e9ebe757ba76
注意:在运行的时候一定要设置这个VM参数
之后利用测试方法获得对想要加密的字符串加密之后的加密串,比如说现在我们想要对 “root” 和 “localhost” 这两个字符串进行加密:
注:使用stringEncryptor类需要引入Jasypt依赖
得到对 “root” 加密之后的加密串:BSxdC2iO2COX+4v7LYuL8w==
得到对 “localhost” 加密之后的加密串: BZEUji+svih5BuEopXMLWLvceWS5reTc
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--使用thymelaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.4</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!--mybatis-spring-boot-starter-->
<!--整合mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--说明:由于springboot整合mybatis版本中默认依赖mybatis 因此不需要额外引入mybati版本,否则会出现冲突`-->
<!--jasypt加密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
注意:使用Jasypt进行加密需要引入Jasypt依赖
<!--jasypt加密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
- 编写配置文件
我们对 “root” 字符串进行了加密,也就是对username、password加密
spring:
thymeleaf:
prefix: classpath:/templates/ # 指定thymeleaf模板前缀目录
suffix: .html # 指定模板的后缀
cache: false # 是否开启thymeleaf缓存,默认为true是开启的,在开发过程中建议关了
#mysql:
# host: ENC(BZEUji+svih5BuEopXMLWLvceWS5reTc)
# 整合mybatis相关配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 指定数据源类型,使用阿里巴巴的数据源
driver-class-name: com.mysql.jdbc.Driver # 指定数据库驱动
url: jdbc:mysql://hocalhost:3306/ems-jsp?characterEncoding=UTF-8 # 指定url
username: ENC(BSxdC2iO2COX+4v7LYuL8w==) # 指定用户名
password: ENC(BSxdC2iO2COX+4v7LYuL8w==) # 指定密码
mybatis:
mapper-locations: classpath:com/baizhi/mapper/*.xml # 指定mapper配置文件的位置
type-aliases-package: com.baizhi.eneity # 为实体类起别名 默认 类名或类名首字母小写
jasypt:
encryptor:
algorithm: PBEWithMD5AndDES # 指定加密方式(加密算法)
注:在编写配置文件时别忘了在入口类上加上 @MapperScan(“com.baizhi.dao”) 扫描dao接口所在的包,在工厂中创建dao接口的对象
@SpringBootApplication
@MapperScan("com.baizhi.dao") // 修饰范围: 用在类上 作用:用来扫描dao接口所在的包 同时将所有dao接口在工厂中创建对象
public class SpringbootDay10Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootDay10Application.class, args);
}
}
- 创建Dao接口
public interface UserDao {
// 查询所有
List<User> findAll();
}
- 编写mapper配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baizhi.dao.UserDao">
<!--查询所有-->
<select id="findAll" resultType="com.baizhi.eneity.User">
select id, username, realname, password, gender from `user`
</select>
</mapper>
- 创建service接口
public interface UserService {
List<User> findAll();
}
- 创建service接口实现类
@Service
@Transactional
public class UserServiceImpl implements UserService{
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public List<User> findAll() {
return userDao.findAll();
}
}
- 创建controller
@RestController
@RequestMapping("users")
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public ResponseEntity<List<User>> users(){
List<User> users = userService.findAll();
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
}
- 运行测试
注:在运行入口类时一定要配置VM参数,为的是设置秘钥,在本次测试中需要设置VM参数
-Djasypt.encryptor.password=cf150b74e4824146ad76e9ebe757ba76
总结:
使用Jasypt进行加密可以很好地避免明文密码泄露问题,但是Jasypt加密最重要的秘钥,所以我们自己的秘钥一定不能泄露出去。