阿里云OSS

  • 对象存储 OSS 是一种海量、安全、低成本、高可靠的云存储服务。
  • 官方文档 –> Java Sdk

①引入oss的依赖到pom文件中

1
2
3
4
5
6
<!-- 阿里云oss依赖 -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>最新版本</version>
</dependency>

②配置application.yml

1
2
3
4
5
6
7
8
#阿里云 OSS配置信息
aliyun:
oss:
file:
endpoint: oss-cn-guangzhou.aliyuncs.com #节点地域
keyid: LTAI5tHuNsemaiLnP3yro4vD #访问id
keysecret: ed1Zxok1SklTEI40HhRKkJeRcQdyWc #访问密码
bucketname: onlineedufile #访问bucketname名

③创建一个类用于读取yml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Component
public class OSSConfigurationClass{
//从yml配置文件中读取下列配置内容
@Value("${aliyun.oss.file.endpoint}")
private String endpoint;
@Value("${aliyun.oss.file.keyid}")
private String keyid;
@Value("${aliyun.oss.file.keysecret}")
private String keysecret;
@Value("${aliyun.oss.file.bucketname}")
private String bucketname;


public static String END_POINT;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
public static String BUCKET_NAME;

/*
在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法。
*/
@PostConstruct
public void init(){
END_POINT = endpoint;
ACCESS_KEY_ID = keyid;
ACCESS_KEY_SECRET = keysecret;
BUCKET_NAME = bucketname;
}
}

④调用逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Service
public class OSSServiceImpl implements OSSService{

//上传头像到oss
@Override
public String uploadFile(MultipartFile file) {
// Endpoint地址
String endpoint = OSSConfigurationClass.END_POINT;
// 阿里云账号AccessKey
String accessKeyId = OSSConfigurationClass.ACCESS_KEY_ID;
String accessKeySecret = OSSConfigurationClass.ACCESS_KEY_SECRET;
// 填写Bucket名称
String bucketName = OSSConfigurationClass.BUCKET_NAME;
// 获取原始文件名称
String objectName = file.getOriginalFilename();

//获取文件后缀
String suffix = objectName.substring(objectName.lastIndexOf("."));
//拼接新的文件名用于上传,防止上传文件名字相同被覆盖
String fileName = UUID.randomUUID().toString();
String fileUploadName = fileName + suffix;

//将文件按照日期进行分类
//获取当前日期
String datePath = new DateTime().toString("yyyy-MM-dd");

//拼接最终上传的文件名
String endUploadPath = datePath + "/" + fileUploadName;

// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

try {
//获取上传文件的输入流
InputStream inputStream = file.getInputStream();
/* 调用oss方法实现上传
第一个参数为bucker名称
第二个参数为上传到oss文件路径和文件名称
第三个参数为文件的上传流
*/
ossClient.putObject(bucketName, endUploadPath, inputStream);

//将上传成功后的路径返回,需要手动拼接,oss没有方法实现
//oss的访问路径为https://onlineedufile.oss-cn-guangzhou.aliyuncs.com/2.jpg
String uploadPath = "https://"+ bucketName+ "." + endpoint + "/" + endUploadPath;

return uploadPath;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
return null;
}
}

EasyExcel

  • EasyExcel是阿里巴巴开源的一个excel处理框架

以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据

一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

  • 使用步骤

①引入EasyExcel的依赖到pom文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
</dependencies>

②创建实体类,需要和excel表数据对应

1
2
3
4
5
6
7
8
9
10
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DataEntity {
//设置excel表头名称和列数索引
@ExcelProperty(value = "学生编号",index = 0)
private Integer num;
@ExcelProperty(value = "学生名字",index = 1)
private String name;
}

③测试写入操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class WriterDemo {
public static void main(String[] args) {
//实现excel写操作

//1.设置写入文件夹地址和excel文件名称
String filename = "D:\\write.xlsx";

/* 2.调用easyexcel中的方法实现写操作
write()方法参数说明:第一个参数为文件路径名称,第二个参数为实体类class
*/
EasyExcel.write(filename, DataEntity.class).sheet("学生列表").doWrite(getList());
}

private static List<DataEntity> getList(){
List<DataEntity> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(new DataEntity(i,"test" + i));
}
return list;
}
}

④测试读入操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//创建监听器进行对excel文件内容的读取
public class ExcelReadListener extends AnalysisEventListener<DataEntity> {
//逐行读取excel的内容
@Override
public void invoke(DataEntity dataEntity, AnalysisContext analysisContext) {
System.out.println("------" + dataEntity + "------");
}

//读取每列表头信息
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("表头内容:" + headMap);
}

//读取完成之后执行
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {

}
}

public class ReadDemo {
public static void main(String[] args) {
//1.设置读取文件夹地址和excel文件名称
String filename = "D:\\write.xlsx";

//实现excel读操作
EasyExcel.read(filename, DataEntity.class,new ExcelReadListener()).sheet().doRead();
}
}

阿里云视频点播

  • 补充一些模糊概念

服务端:后端接口 客户端:浏览器、ios、安卓

服务端API:使用官方提供好的固定地址,只需要调用这个地址并向这个地址传递参数,即可实现功能,

使用了HttpClient技术模拟浏览器发送请求

服务端SDK:sdk是对api的一种封装,一般只需要导入依赖后,通过调用官方提供的类或接口中的方法

即可实现功能,操作更加简便

  • 视频点播实现步骤

①创建子模块并引入相关依赖(老版本引入方式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!--aliyun-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>${aliyun-java-sdk-core.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-vod</artifactId>
<version>${aliyun-java-sdk-vod.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-vod-upload</artifactId>
<version>${aliyun-java-vod-upload.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-sdk-vod-upload</artifactId>
<version>${aliyun-sdk-vod-upload.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>${json.version}</version>
</dependency>

②初始化DefaultAcsClient对象

1
2
3
4
5
6
7
8
9
public class VodInitObject {
//填入AccessKey信息
public static DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) throws ClientException {
String regionId = "cn-shanghai"; // 点播服务接入地域
DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
return client;
}
}

③根据视频id获取视频播放地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//根据视频id获取视频地址
public static void GetPlayUrl() throws Exception{
//1.创建初始化对象
DefaultAcsClient defaultAcsClient = VodInitObject.initVodClient("LTAI5tHuNsemaiLnP3yro4vD", "ed1Zxok1SklTEI40HhRKkJeRcQdyWc");
//2.创建response对象和request对象
GetPlayInfoResponse response = new GetPlayInfoResponse();
GetPlayInfoRequest request = new GetPlayInfoRequest();

//3.向request对象中设置视频id
request.setVideoId("3e0d11654ac6483f9f0311f8a92fecff");

//4.将初始化对象的方法,传入request对象,获取数据并封装到response对象中
response = defaultAcsClient.getAcsResponse(request);

List<GetPlayInfoResponse.PlayInfo> playInfoList = response.getPlayInfoList();
//播放地址
for (GetPlayInfoResponse.PlayInfo playInfo : playInfoList) {
System.out.print("PlayInfo.PlayURL = " + playInfo.getPlayURL() + "\n");
}
//Base信息
System.out.print("VideoBase.Title = " + response.getVideoBase().getTitle() + "\n");
}

④根据视频id获取视频播放凭证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//根据视频id获取视频凭证
public static void getPlayAuth() throws Exception{
//1.创建初始化对象
DefaultAcsClient defaultAcsClient = VodInitObject.initVodClient("LTAI5tHuNsemaiLnP3yro4vD", "ed1Zxok1SklTEI40HhRKkJeRcQdyWc");
//2.创建response对象和request对象
GetVideoPlayAuthResponse response = new GetVideoPlayAuthResponse();
GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();

//3.向request对象中设置视频id
request.setVideoId("3e0d11654ac6483f9f0311f8a92fecff");

//4.调用初始化对象中的方法,将request对象传入获取视频信息并赋值给response对象
response = defaultAcsClient.getAcsResponse(request);

//播放凭证
System.out.print("PlayAuth = " + response.getPlayAuth() + "\n");
//VideoMeta信息
System.out.print("VideoMeta.Title = " + response.getVideoMeta().getTitle() + "\n");
}

⑤测试视频上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void  uploadVideo(){
String accessKeyId = "LTAI5tHuNsemaiLnP3yro4vD";
String accessKeySecret = "ed1Zxok1SklTEI40HhRKkJeRcQdyWc";
String title = "bluesky-uploadBySdk"; //设置上传之后的文件名称
String fileName = "C:/Users/hcxs1986/Desktop/解压专用文件夹/bluesky.mp4"; //本地文件路径和名称
UploadVideoRequest request = new UploadVideoRequest(accessKeyId, accessKeySecret, title, fileName);
/* 可指定分片上传时每个分片的大小,默认为2M字节 */
request.setPartSize(2 * 1024 * 1024L);
/* 可指定分片上传时的并发线程数,默认为1,(注:该配置会占用服务器CPU资源,需根据服务器情况指定)*/
request.setTaskNum(1);
// request.setEcsRegionId("cn-shanghai");
UploadVideoImpl uploader = new UploadVideoImpl();
UploadVideoResponse response = uploader.uploadVideo(request);
if (response.isSuccess()) {
System.out.print("VideoId=" + response.getVideoId() + "\n");
} else {
/* 如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因 */
System.out.print("VideoId=" + response.getVideoId() + "\n");
System.out.print("ErrorCode=" + response.getCode() + "\n");
System.out.print("ErrorMessage=" + response.getMessage() + "\n");
}
}

JWT

JWT全称Json Web Token

  • 与token的区别是什么?

token是根据自定义规则生成的字符串,而JWT是根据官方通用预定规则生成的字符串,包含用户信息

  • JWT令牌类型

session是透明令牌的一种,jwt是自包含令牌的一种

  • JWT字符串

有三个不同颜色的部分组成整个JWT字符串

  • 使用JWT

①在pom文件中引入依赖

1
2
3
4
5
6
7
<dependencies>
<!-- JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>

②创建JWT工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class JwtUtils {

public static final long EXPIRE = 1000 * 60 * 60 * 24; //定义token的过期时间
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO"; //密钥

/**
* Description : 生成token字符串
* @date 2022/8/28
* @param id 用户id
* @param nickname 用户名称
**/
public static String getJwtToken(String id, String nickname){

String JwtToken = Jwts.builder()
//设置token的头信息
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
//设置token的过期时间
.setSubject("onlineedu-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
//设置token的主体部分,存储用户信息
.claim("id", id)
.claim("nickname", nickname)
//设置签名哈希
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}

/**
* 判断token是否存在与有效
* @param jwtToken 已经生成的token字符串
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}

/**
* 判断token是否存在与有效
* @param request 当前请求对象
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}

/**
* 根据token获取id
* @param request 当前请求对象
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody(); //获取token字符串的主体部分
return (String)claims.get("id"); //根据id取值
}
}

OAuth2

  • OAuth2是什么?

OAuth2是针对特定问题的一种解决方案

  • OAuth2解决了哪些问题?

1、开放系统间授权

通过授权给予某些服务提供对其账户的限时访问权限

2、分布式访问问题

OAuth2是对分布式访问问题的一种解决方案,通过令牌机制按照一定规则生成字符串,

且字符串中也包含用户信息

  • OAuth2这么看与JWT差不多,那它们有什么区别?

OAuth2只是提供了一个具体解决的方案,并没有向jwt一样指定具体生成规则,

而它的规则是需要我们自己规定,具体细节依旧需要自己实现,两者的关系就

类似于接口和实现类,OAuth2相当于接口的身份,JWT相当于实现类的身份

  • 对OAuth2的误解


Canal

  • canal是什么?

canal是个数据同步工具,是阿里巴巴旗下开源项目,但仅支持mysql数据库

  • 为什么要用canal?

在多个微服务对应多个数据库的情况下,某个微服务需要获取另一个微服务的数据库数据时,

采用远程调用也能实现,但耦合度较高,而使用canal可以将另一个微服务的数据库中表数据

同步到当前微服务的数据库中。

  • 如何使用canal?

①需要两台服务器中有一个相同的数据库,且数据库和数据表的名称和结构都一样

②在需要被同步的服务器中安装canal数据同步工具

③检查需要被同步的服务器中mysql数据库的binlog功能是否已开启,确认数据库能够远程访问

④创建一个模块进行测试

⑤修改配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 10002

spring:
application:
name: canal-client
profiles:
active: dev
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/canal?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username: root
password: root

⑥创建一个canal的客户端类

  • 数据同步实现的底层原理是主启动的线程会不断监听被同步数据库的变化,通过判断batchId和size的值,

    当其中之一出现满足的情况,线程进入休眠,否则执行数据处理dataHandle的方法,而在这个方法中又

    对处理类型进行了判断,增加就执行insert对应的方法,其他也是如此,就以insert的方法而言,其内部

    是通过拼接sql语句并放到queue队列中逐个排队执行,从而实现数据同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package canal.client;

import org.springframework.stereotype.Component;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry.*;
import com.alibaba.otter.canal.protocol.Message;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.net.InetSocketAddress;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
* @author hcxs1986
* @version 1.0
* @description: TODO
* @date 2022/9/3 20:53
*/
@Component
public class CanalClient {
//sql队列
private Queue<String> SQL_QUEUE = new ConcurrentLinkedQueue<>();

@Resource
private DataSource dataSource;

/**
* canal入库方法
*/
public void run() {

CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.231.134",
11111), "example", "", "");
int batchSize = 1000;
try {
connector.connect();
connector.subscribe(".*\\..*");
connector.rollback();
try {
while (true) {
//尝试从master那边拉去数据batchSize条记录,有多少取多少
Message message = connector.getWithoutAck(batchSize);
long batchId = message.getId();
int size = message.getEntries().size();
if (batchId == -1 || size == 0) {
Thread.sleep(1000);
} else {
dataHandle(message.getEntries());
}
connector.ack(batchId);

//当队列里面堆积的sql大于一定数值的时候就模拟执行
if (SQL_QUEUE.size() >= 1) {
executeQueueSql();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
} finally {
connector.disconnect();
}
}

/**
* 模拟执行队列里面的sql语句
*/
public void executeQueueSql() {
int size = SQL_QUEUE.size();
for (int i = 0; i < size; i++) {
String sql = SQL_QUEUE.poll();
System.out.println("[sql]----> " + sql);

this.execute(sql.toString());
}
}

/**
* 数据处理
*
* @param entrys
*/
private void dataHandle(List<Entry> entrys) throws InvalidProtocolBufferException {
for (Entry entry : entrys) {
if (EntryType.ROWDATA == entry.getEntryType()) {
RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());
EventType eventType = rowChange.getEventType();
if (eventType == EventType.DELETE) {
saveDeleteSql(entry);
} else if (eventType == EventType.UPDATE) {
saveUpdateSql(entry);
} else if (eventType == EventType.INSERT) {
saveInsertSql(entry);
}
}
}
}

/**
* 保存更新语句
*
* @param entry
*/
private void saveUpdateSql(Entry entry) {
try {
RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());
List<RowData> rowDatasList = rowChange.getRowDatasList();
for (RowData rowData : rowDatasList) {
List<Column> newColumnList = rowData.getAfterColumnsList();
StringBuffer sql = new StringBuffer("update " + entry.getHeader().getTableName() + " set ");
for (int i = 0; i < newColumnList.size(); i++) {
sql.append(" " + newColumnList.get(i).getName()
+ " = '" + newColumnList.get(i).getValue() + "'");
if (i != newColumnList.size() - 1) {
sql.append(",");
}
}
sql.append(" where ");
List<Column> oldColumnList = rowData.getBeforeColumnsList();
for (Column column : oldColumnList) {
if (column.getIsKey()) {
//暂时只支持单一主键
sql.append(column.getName() + "=" + column.getValue());
break;
}
}
SQL_QUEUE.add(sql.toString());
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}

/**
* 保存删除语句
*
* @param entry
*/
private void saveDeleteSql(Entry entry) {
try {
RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());
List<RowData> rowDatasList = rowChange.getRowDatasList();
for (RowData rowData : rowDatasList) {
List<Column> columnList = rowData.getBeforeColumnsList();
StringBuffer sql = new StringBuffer("delete from " + entry.getHeader().getTableName() + " where ");
for (Column column : columnList) {
if (column.getIsKey()) {
//暂时只支持单一主键
sql.append(column.getName() + "=" + column.getValue());
break;
}
}
SQL_QUEUE.add(sql.toString());
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}

/**
* 保存插入语句
*
* @param entry
*/
private void saveInsertSql(Entry entry) {
try {
RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());
List<RowData> rowDatasList = rowChange.getRowDatasList();
for (RowData rowData : rowDatasList) {
List<Column> columnList = rowData.getAfterColumnsList();
StringBuffer sql = new StringBuffer("insert into " + entry.getHeader().getTableName() + " (");
for (int i = 0; i < columnList.size(); i++) {
sql.append(columnList.get(i).getName());
if (i != columnList.size() - 1) {
sql.append(",");
}
}
sql.append(") VALUES (");
for (int i = 0; i < columnList.size(); i++) {
sql.append("'" + columnList.get(i).getValue() + "'");
if (i != columnList.size() - 1) {
sql.append(",");
}
}
sql.append(")");
SQL_QUEUE.add(sql.toString());
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}

/**
* 入库
* @param sql
*/
public void execute(String sql) {
Connection con = null;
try {
if(null == sql) return;
con = dataSource.getConnection();
QueryRunner qr = new QueryRunner();
int row = qr.execute(con, sql);
System.out.println("update: "+ row);
} catch (SQLException e) {
e.printStackTrace();
} finally {
DbUtils.closeQuietly(con);
}
}
}

⑦启动类实现CommandLineRunner接口并监听被同步服务的变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@SpringBootApplication
public class CanalClient10000 implements CommandLineRunner {
@Resource
private CanalClient canalClient;

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

@Override
public void run(String... strings) throws Exception {
//项目启动,执行canal客户端监听
canalClient.run();
}
}

Spring Security

  • Spring Security是什么?

Spring Security是基于Spring开发的框架,提供了一套web应用安全性的完整解决方案。

Web应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分

Spring Security其实就是使用了filter,对请求的路径进行拦截和过滤。

  • 用户认证(Authentication) 与 用户授权(Authorization)

①用户认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。

一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。

②用户授权指验证某个用户是否有权限执行某个操作。系统会为不同的用户分配不同的角色,

而每个角色则对应一系列的权限。

  • 认证与授权实现思路

①用户输入账户密码后,经数据库查询正确后,会获取该用户的权限列表

②以该用户名为key,该用户的权限列表为value放入redis缓存中

③根据包含用户名在内的某种规则生成token并返回

④将返回的token放入cookie中,且在每次请求中都会将token从cookie中取出放在请求头header上

⑤在接收到请求后,spring security会从请求头中取出token,根据token获取用户名,根据获取到

用户名到redis中查询用户权限列表。

⑥根据从redis中查到的权限列表对该用户进行对应的权限授权