This commit is contained in:
Jesse-Ma
2022-11-22 13:05:47 +08:00
committed by GitHub
parent 94d4744202
commit 0d9b481d81
15 changed files with 673 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
package com.flagnote.note;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableAutoConfiguration
@RestController
public class NoteApplication {
public static void main(String[] args) {
SpringApplication.run(NoteApplication.class, args);
}
}

View File

@@ -0,0 +1,127 @@
package com.flagnote.note.controller;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.flagnote.note.entity.KeyMeta;
import com.flagnote.note.entity.Note;
import com.flagnote.note.entity.NoteMeta;
import com.flagnote.note.service.NoteService;
import com.flagnote.note.utils.BizKeyUtils;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
public class NoteController {
@Autowired
private NoteService noteService;
@RequestMapping(value = "/note/keyMeta", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public KeyMeta getKeyMeta() {
KeyMeta c = new KeyMeta();
String key = BizKeyUtils.getKey();
c.setKey(key);
c.setCipher(BizKeyUtils.getCipher(key));
c.setServerTime(new Date().getTime());
return c;
}
@RequestMapping(value = "/note/{key:[abcdefghijkmnopqrstuvwxyz23456789]{16}}/noteMeta", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public NoteMeta getNoteMeta(@PathVariable("key") String key) {
NoteMeta meta = new NoteMeta();
Note note = noteService.getNote(key);
if (null == note) {
return meta;
}
meta.setServerTime(new Date().getTime());
meta.setKey(key);
meta.setLock(note.getLock());
meta.setMd5(note.getMd5());
meta.setTtl(note.getTtl());
return meta;
}
@RequestMapping(value = "/note/{key:[abcdefghijkmnopqrstuvwxyz23456789]{16}}", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void getNote(@PathVariable("key") String key, HttpServletResponse response) {
Note note = noteService.getNote(key);
response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + key + "\"");
if(null==note) {
return;
}
OutputStream out = null;
try {
out = response.getOutputStream();
} catch (IOException e) {
log.error("获取写入流失败", e);
return;
}
try {
out.write(note.getTextBytes());
out.flush();
} catch (IOException e) {
log.error("写入流失败", e);
} finally {
if (null != out) {
try {
out.close();
} catch (IOException e) {
log.error("关闭流失败", e);
}
}
}
}
@RequestMapping(value = "/note/{key:[abcdefghijkmnopqrstuvwxyz23456789]{16}}", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String saveNote(@PathVariable("key") String key, @RequestPart("file") MultipartFile file,
@RequestParam("lock") Integer lock, @RequestParam("md5") String md5) throws IOException {
Date pushTime = new Date();
Note note = new Note();
note.setKey(key);
note.setTextBytes(file.getBytes());
note.setLock(lock);
note.setMd5(md5);
note.setPushTime(pushTime);
noteService.saveNote(note);
return key;
}
@RequestMapping(value = "/note/{key:[abcdefghijkmnopqrstuvwxyz23456789]{16}}/delete", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public String deleteNote(@PathVariable("key") String key) {
noteService.deleteNote(key);
return key;
}
}

View File

@@ -0,0 +1,20 @@
package com.flagnote.note.entity;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
@Data
public class KeyMeta implements Serializable {/**
*
*/
private static final long serialVersionUID = -2816061962747625732L;
private String key;
private String cipher;
private Long serverTime;
}

View File

@@ -0,0 +1,47 @@
package com.flagnote.note.entity;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
@Data
public class Note implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5301542293133170670L;
private String id;
private String key;
private String text;
private String md5;
private Integer lock;
private String cipher;
private Integer state;
private Date pushTime;
private Date expireTime;
private Date retentionTime;
private byte[] textBytes;
public Long getTtl() {
if (null == pushTime || null == expireTime) {
return null;
}
Date now = new Date();
return expireTime.getTime() - now.getTime();
}
}

View File

@@ -0,0 +1,28 @@
package com.flagnote.note.entity;
import java.io.Serializable;
import lombok.Data;
@Data
public class NoteMeta implements Serializable {/**
*
*/
private static final long serialVersionUID = -8234044213813670440L;
/**
*
*/
private String key;
private String cipher;
private Integer lock;
private String md5;
private Long serverTime;
private Long ttl;
}

View File

@@ -0,0 +1,24 @@
package com.flagnote.note.entity;
import java.io.Serializable;
import lombok.Data;
@Data
public class Secret implements Serializable {
/**
*
*/
private static final long serialVersionUID = -4125184146529253566L;
private String key;
private String password;
private String secret;
}

View File

@@ -0,0 +1,11 @@
package com.flagnote.note.repository;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.flagnote.note.entity.Note;
@Repository
public interface NoteMongoRepository extends MongoRepository<Note, String> {
}

View File

@@ -0,0 +1,13 @@
package com.flagnote.note.service;
import com.flagnote.note.entity.Note;
public interface NoteService {
Note getNote(String key);
void saveNote(Note note);
void deleteNote(String key);
}

View File

@@ -0,0 +1,70 @@
package com.flagnote.note.service;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import com.flagnote.note.entity.Note;
import com.flagnote.note.repository.NoteMongoRepository;
import com.flagnote.note.utils.BizKeyUtils;
import cn.hutool.core.date.DateUtil;
@Service
public class NoteServiceImpl implements NoteService {
@Autowired
private NoteMongoRepository noteMongoRepository;
@Override
public Note getNote(String key) {
String mixKey = BizKeyUtils.mixKey(key);
Note note = noteMongoRepository.findById(mixKey).orElse(null);
if (null == note) {
return null;
}
if (note.getState() != 1) {
return null;
}
if (note.getExpireTime().getTime() <= new Date().getTime()) {
return null;
}
return note;
}
@Override
public void saveNote(Note note) {
String mixKey = BizKeyUtils.mixKey(note.getKey());
note.setId(mixKey);
note.setKey(mixKey);
note.setExpireTime(DateUtil.offsetHour(note.getPushTime(), 1));
note.setRetentionTime(DateUtil.offsetSecond(note.getPushTime(), 4000 * 3600));
note.setState(1);
noteMongoRepository.save(note);
}
@Override
public void deleteNote(String key) {
String mixKey = BizKeyUtils.mixKey(key);
noteMongoRepository.deleteById(mixKey);
}
}

View File

@@ -0,0 +1,95 @@
package com.flagnote.note.utils;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import org.springframework.util.DigestUtils;
public class BizKeyUtils {
public static final String MIX_STRING = "6v8muhqp8ta45ncsyi8y";
public static final String RANGE_STRING = "abcdefhikmnopqstuvwxyz23456789";
public static String getKey() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 13; i++) {
sb.append(RANGE_STRING.charAt(RandomUtils.nextInt(30)));
}
sb.append(getFilteredKey(md5(sb.toString() + MIX_STRING + getPrefixTime())));
return sb.toString();
}
public static String getFilteredKey(String key) {
String result = "";
for (int i = 0; i < key.length(); i++) {
String ts = String.valueOf(key.charAt(i));
if (RANGE_STRING.indexOf(ts) >= 0) {
result += ts;
}
if (result.length() == 3) {
return result;
}
}
for (int i = 1; i < 3; i++) {
result += "x";
}
return result;
}
public static Boolean validateKey(String key) {
String oKey = key.substring(0, 13);
String cKey = oKey + getFilteredKey(md5(oKey + MIX_STRING + getPrefixTime()));
if (cKey.equals(key)) {
return true;
}
cKey = oKey + getFilteredKey(md5(oKey + MIX_STRING + (getPrefixTime() - 1)));
if (cKey.equals(key)) {
return true;
}
cKey = oKey + getFilteredKey(md5(oKey + MIX_STRING + (getPrefixTime() + 1)));
if (cKey.equals(key)) {
return true;
}
return false;
}
public static String mixKey(String key) {
return md5(key + md5(MIX_STRING + key));
}
public static String md5(String text) {
try {
return DigestUtils.md5DigestAsHex(text.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("无法生成MD5");
}
}
public static Integer getPrefixTime() {
return Integer.parseInt(String.valueOf(new Date().getTime()).substring(0, 4));
}
public static String getCipher(String key) {
return md5(key + MIX_STRING + key);
}
public static String getSecretKey(String key, String password) {
return md5(key + md5(MIX_STRING + password));
}
}

View File

@@ -0,0 +1,112 @@
package com.flagnote.note.utils;
import java.io.IOException;
import java.text.SimpleDateFormat;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class JsonUtils {
private static ObjectMapper om = new ObjectMapper();
static {
// 对象的所有字段全部列入还是其他的选项可以忽略null等
om.setSerializationInclusion(Include.ALWAYS);
// 设置Date类型的序列化及反序列化格式
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 忽略空Bean转json的错误
om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// 忽略未知属性防止json字符串中存在java对象中不存在对应属性的情况出现错误
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 注册一个时间序列化及反序列化的处理模块用于解决jdk8中localDateTime等的序列化问题
om.registerModule(new JavaTimeModule());
}
/**
* 对象 => json字符串
*
* @param obj 源对象
*/
public static <T> String toJson(T obj) {
String json = null;
if (obj != null) {
try {
json = om.writeValueAsString(obj);
} catch (JsonProcessingException e) {
log.warn(e.getMessage(), e);
throw new IllegalArgumentException(e.getMessage());
}
}
return json;
}
/**
* json字符串 => 对象
*
* @param json 源json串
* @param clazz 对象类
* @param <T> 泛型
*/
public static <T> T parse(String json, Class<T> clazz) {
return parse(json, clazz, null);
}
/**
* json字符串 => 对象
*
* @param json 源json串
* @param type 对象类型
* @param <T> 泛型
*/
public static <T> T parse(String json, TypeReference<T> type) {
return parse(json, null, type);
}
/**
* json => 对象处理方法
* <br>
* 参数clazz和type必须一个为null另一个不为null
* <br>
* 此方法不对外暴露访问权限为private
*
* @param json 源json串
* @param clazz 对象类
* @param type 对象类型
* @param <T> 泛型
*/
private static <T> T parse(String json, Class<T> clazz, TypeReference<T> type) {
T obj = null;
if (StringUtils.hasLength(json)) {
try {
if (clazz != null) {
obj = om.readValue(json, clazz);
} else {
obj = om.readValue(json, type);
}
} catch (IOException e) {
log.warn(e.getMessage(), e);
throw new IllegalArgumentException(e.getMessage());
}
}
return obj;
}
}

View File

@@ -0,0 +1,15 @@
package com.flagnote.note.utils;
import java.util.concurrent.ThreadLocalRandom;
public class RandomUtils {
public static ThreadLocalRandom getRandom() {
return ThreadLocalRandom.current();
}
public static Integer nextInt(Integer num) {
return getRandom().nextInt(num);
}
}