阿里云OSS
- 对象存储 OSS 是一种海量、安全、低成本、高可靠的云存储服务。
- 官方文档 –> Java Sdk
①引入oss的依赖到pom文件中
1 2 3 4 5 6
| <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>最新版本</version> </dependency>
|
②配置application.yml
1 2 3 4 5 6 7 8
| aliyun: oss: file: endpoint: oss-cn-guangzhou.aliyuncs.com keyid: LTAI5tHuNsemaiLnP3yro4vD keysecret: ed1Zxok1SklTEI40HhRKkJeRcQdyWc bucketname: onlineedufile
|
③创建一个类用于读取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{ @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;
@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{
@Override public String uploadFile(MultipartFile file) { String endpoint = OSSConfigurationClass.END_POINT; String accessKeyId = OSSConfigurationClass.ACCESS_KEY_ID; String accessKeySecret = OSSConfigurationClass.ACCESS_KEY_SECRET; 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;
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try { InputStream inputStream = file.getInputStream();
ossClient.putObject(bucketName, endUploadPath, inputStream);
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> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.1</version> </dependency> <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 { @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) {
String filename = "D:\\write.xlsx";
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
| public class ExcelReadListener extends AnalysisEventListener<DataEntity> { @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) { String filename = "D:\\write.xlsx";
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
| <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 { 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
| public static void GetPlayUrl() throws Exception{ DefaultAcsClient defaultAcsClient = VodInitObject.initVodClient("LTAI5tHuNsemaiLnP3yro4vD", "ed1Zxok1SklTEI40HhRKkJeRcQdyWc"); GetPlayInfoResponse response = new GetPlayInfoResponse(); GetPlayInfoRequest request = new GetPlayInfoRequest();
request.setVideoId("3e0d11654ac6483f9f0311f8a92fecff");
response = defaultAcsClient.getAcsResponse(request);
List<GetPlayInfoResponse.PlayInfo> playInfoList = response.getPlayInfoList(); for (GetPlayInfoResponse.PlayInfo playInfo : playInfoList) { System.out.print("PlayInfo.PlayURL = " + playInfo.getPlayURL() + "\n"); } 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
| public static void getPlayAuth() throws Exception{ DefaultAcsClient defaultAcsClient = VodInitObject.initVodClient("LTAI5tHuNsemaiLnP3yro4vD", "ed1Zxok1SklTEI40HhRKkJeRcQdyWc"); GetVideoPlayAuthResponse response = new GetVideoPlayAuthResponse(); GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();
request.setVideoId("3e0d11654ac6483f9f0311f8a92fecff");
response = defaultAcsClient.getAcsResponse(request);
System.out.print("PlayAuth = " + response.getPlayAuth() + "\n"); 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); request.setPartSize(2 * 1024 * 1024L); request.setTaskNum(1); UploadVideoImpl uploader = new UploadVideoImpl(); UploadVideoResponse response = uploader.uploadVideo(request); if (response.isSuccess()) { System.out.print("VideoId=" + response.getVideoId() + "\n"); } else { 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是根据自定义规则生成的字符串,而JWT是根据官方通用预定规则生成的字符串,包含用户信息
session是透明令牌的一种,jwt是自包含令牌的一种
有三个不同颜色的部分组成整个JWT字符串
①在pom文件中引入依赖
1 2 3 4 5 6 7
| <dependencies> <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; public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder() .setHeaderParam("typ", "JWT") .setHeaderParam("alg", "HS256") .setSubject("onlineedu-user") .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) .claim("id", id) .claim("nickname", nickname) .signWith(SignatureAlgorithm.HS256, APP_SECRET) .compact(); return JwtToken; }
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; }
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; }
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(); return (String)claims.get("id"); } }
|
OAuth2
OAuth2是针对特定问题的一种解决方案
1、开放系统间授权
通过授权给予某些服务提供对其账户的限时访问权限
2、分布式访问问题
OAuth2是对分布式访问问题的一种解决方案,通过令牌机制按照一定规则生成字符串,
且字符串中也包含用户信息
- OAuth2这么看与JWT差不多,那它们有什么区别?
OAuth2只是提供了一个具体解决的方案,并没有向jwt一样指定具体生成规则,
而它的规则是需要我们自己规定,具体细节依旧需要自己实现,两者的关系就
类似于接口和实现类,OAuth2相当于接口的身份,JWT相当于实现类的身份
Canal
canal是个数据同步工具,是阿里巴巴旗下开源项目,但仅支持mysql数据库
在多个微服务对应多个数据库的情况下,某个微服务需要获取另一个微服务的数据库数据时,
采用远程调用也能实现,但耦合度较高,而使用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;
@Component public class CanalClient { private Queue<String> SQL_QUEUE = new ConcurrentLinkedQueue<>();
@Resource private DataSource dataSource;
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) { 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);
if (SQL_QUEUE.size() >= 1) { executeQueueSql(); } } } catch (InterruptedException e) { e.printStackTrace(); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } } finally { connector.disconnect(); } }
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()); } }
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); } } } }
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(); } }
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(); } }
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(); } }
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 { canalClient.run(); } }
|
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中查到的权限列表对该用户进行对应的权限授权