feat: adding controllers and base
This commit is contained in:
parent
41b5061436
commit
5ce5332891
@ -38,6 +38,7 @@ dependencies {
|
||||
implementation("org.springframework.shell:spring-shell-starter")
|
||||
// https://mvnrepository.com/artifact/com.mybatis-flex/mybatis-flex-spring-boot3-starter
|
||||
implementation("com.mybatis-flex:mybatis-flex-spring-boot3-starter:1.9.7")
|
||||
annotationProcessor("com.mybatis-flex:mybatis-flex-processor:1.9.7")
|
||||
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
|
||||
implementation("com.alibaba.fastjson2:fastjson2:2.0.53")
|
||||
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2-extension
|
||||
@ -55,7 +56,7 @@ dependencies {
|
||||
// https://mvnrepository.com/artifact/com.mysql/mysql-connector-j
|
||||
implementation("com.mysql:mysql-connector-j:9.0.0")
|
||||
compileOnly("org.projectlombok:lombok")
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
/*developmentOnly("org.springframework.boot:spring-boot-devtools")*/
|
||||
annotationProcessor("org.projectlombok:lombok")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc")
|
||||
|
@ -1,14 +1,27 @@
|
||||
package cn.wzpmc.filemanager;
|
||||
|
||||
import com.mybatisflex.core.audit.AuditManager;
|
||||
import com.mybatisflex.core.audit.ConsoleMessageCollector;
|
||||
import com.mybatisflex.core.audit.MessageCollector;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||
import org.springframework.shell.command.annotation.EnableCommand;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@SpringBootApplication
|
||||
@ConfigurationPropertiesScan(basePackages = {"cn.wzpmc.filemanager.config"})
|
||||
@MapperScan("cn.wzpmc.filemanager.mapper")
|
||||
@EnableTransactionManagement
|
||||
@EnableCommand
|
||||
public class FileManagerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
//开启审计功能
|
||||
AuditManager.setAuditEnable(true);
|
||||
MessageCollector collector = new ConsoleMessageCollector();
|
||||
AuditManager.setMessageCollector(collector);
|
||||
SpringApplication.run(FileManagerApplication.class, args);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
package cn.wzpmc.filemanager.annotation;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.user.enums.Auth;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AuthorizationRequired {
|
||||
Auth level() default Auth.user;
|
||||
boolean force() default false;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.wzpmc.filemanager.commands;
|
||||
|
||||
import cn.wzpmc.filemanager.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
import org.springframework.shell.standard.ShellMethod;
|
||||
|
||||
@ShellComponent
|
||||
public class AuthorizationCommands {
|
||||
private final UserService userService;
|
||||
@Autowired
|
||||
public AuthorizationCommands(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
@ShellMethod("创建一个密钥")
|
||||
public void key(){
|
||||
this.userService.genInviteCode();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.wzpmc.filemanager.configuration;
|
||||
|
||||
import cn.wzpmc.filemanager.utils.AuthorizationArgumentResolver;
|
||||
import cn.wzpmc.filemanager.utils.AuthorizationHandlerInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class AuthorizationConfiguration implements WebMvcConfigurer {
|
||||
private final AuthorizationArgumentResolver authorizationArgumentResolver;
|
||||
private final AuthorizationHandlerInterceptor authorizationHandlerInterceptor;
|
||||
@Autowired
|
||||
public AuthorizationConfiguration(AuthorizationArgumentResolver authorizationArgumentResolver, AuthorizationHandlerInterceptor authorizationHandlerInterceptor) {
|
||||
this.authorizationArgumentResolver = authorizationArgumentResolver;
|
||||
this.authorizationHandlerInterceptor = authorizationHandlerInterceptor;
|
||||
}
|
||||
@Override
|
||||
public void addArgumentResolvers(@NonNull List<HandlerMethodArgumentResolver> resolvers) {
|
||||
WebMvcConfigurer.super.addArgumentResolvers(resolvers);
|
||||
resolvers.add(authorizationArgumentResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(@NonNull InterceptorRegistry registry) {
|
||||
WebMvcConfigurer.super.addInterceptors(registry);
|
||||
registry.addInterceptor(authorizationHandlerInterceptor);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.wzpmc.filemanager.configuration;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.files.ChunkChecked;
|
||||
import cn.wzpmc.filemanager.entities.files.ChunkReady;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
@Configuration
|
||||
public class RedisConfiguration {
|
||||
private final RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
@Autowired
|
||||
public RedisConfiguration(RedisConnectionFactory redisConnectionFactory) {
|
||||
this.redisConnectionFactory = redisConnectionFactory;
|
||||
}
|
||||
@Bean
|
||||
public RedisTemplate<String, ChunkReady> uploadMapper() {
|
||||
RedisTemplate<String, ChunkReady> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(redisConnectionFactory);
|
||||
return template;
|
||||
}
|
||||
@Bean
|
||||
public RedisTemplate<String, ChunkChecked> chunkUploadMapper() {
|
||||
RedisTemplate<String, ChunkChecked> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(redisConnectionFactory);
|
||||
return template;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package cn.wzpmc.filemanager.controller;
|
||||
|
||||
import cn.wzpmc.filemanager.annotation.AuthorizationRequired;
|
||||
import cn.wzpmc.filemanager.entities.Result;
|
||||
import cn.wzpmc.filemanager.entities.files.CheckChunkResponse;
|
||||
import cn.wzpmc.filemanager.entities.files.DoneUploadRequest;
|
||||
import cn.wzpmc.filemanager.entities.files.FileObject;
|
||||
import cn.wzpmc.filemanager.entities.files.PrepareUploadRequest;
|
||||
import cn.wzpmc.filemanager.entities.vo.UserVo;
|
||||
import cn.wzpmc.filemanager.service.FileService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/file")
|
||||
public class FileController {
|
||||
private final FileService fileService;
|
||||
|
||||
@Autowired
|
||||
public FileController(FileService fileService) {
|
||||
this.fileService = fileService;
|
||||
}
|
||||
@PostMapping("/prepare")
|
||||
public Result<String> prepareUploadChunks(@RequestBody PrepareUploadRequest prepareUploadRequest, @AuthorizationRequired UserVo user){
|
||||
return fileService.prepareUploadChunks(prepareUploadRequest, user);
|
||||
}
|
||||
@PutMapping("/chunk/upload")
|
||||
public Result<Void> uploadChunk(MultipartFile file,@RequestParam String id){
|
||||
return fileService.uploadChunk(file, id);
|
||||
}
|
||||
@GetMapping("/chunk/check")
|
||||
public Result<CheckChunkResponse> checkChunk(@RequestParam String hash, @RequestParam String id, @RequestParam long index){
|
||||
return fileService.checkChunk(hash, id, index);
|
||||
}
|
||||
@PostMapping("/done")
|
||||
public Result<FileObject> doneUpload(@RequestBody DoneUploadRequest data){
|
||||
return fileService.doneUpload(data.getId());
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package cn.wzpmc.filemanager.controller;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.Result;
|
||||
import cn.wzpmc.filemanager.exceptions.AuthorizationException;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(AuthorizationException.class)
|
||||
public ResponseEntity<Result<Void>> handleAuthorizationException(AuthorizationException e) {
|
||||
Result<Void> result = e.getResult();
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
}
|
@ -1,12 +1,35 @@
|
||||
package cn.wzpmc.filemanager.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import cn.wzpmc.filemanager.annotation.AuthorizationRequired;
|
||||
import cn.wzpmc.filemanager.entities.Result;
|
||||
import cn.wzpmc.filemanager.entities.user.UserLoginRequest;
|
||||
import cn.wzpmc.filemanager.entities.user.UserRegisterRequest;
|
||||
import cn.wzpmc.filemanager.entities.user.enums.Auth;
|
||||
import cn.wzpmc.filemanager.entities.vo.UserVo;
|
||||
import cn.wzpmc.filemanager.service.UserService;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
public class UserController {
|
||||
private final UserService userService;
|
||||
@Autowired
|
||||
public UserController(UserService userService) {
|
||||
this.userService= userService;
|
||||
}
|
||||
@PostMapping("/login")
|
||||
public
|
||||
public void login(@RequestBody UserLoginRequest loginRequest, HttpServletResponse response) {
|
||||
userService.login(loginRequest, response);
|
||||
}
|
||||
@PutMapping("/register")
|
||||
public void register(@RequestBody UserRegisterRequest registerRequest, HttpServletResponse response) {
|
||||
userService.register(registerRequest, response);
|
||||
}
|
||||
@GetMapping("/invite")
|
||||
@AuthorizationRequired(level = Auth.admin)
|
||||
public Result<String> invite(){
|
||||
return userService.invite();
|
||||
}
|
||||
}
|
13
src/main/java/cn/wzpmc/filemanager/entities/Page.java
Normal file
13
src/main/java/cn/wzpmc/filemanager/entities/Page.java
Normal file
@ -0,0 +1,13 @@
|
||||
package cn.wzpmc.filemanager.entities;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class Page<T> {
|
||||
private int total;
|
||||
private List<T> data;
|
||||
}
|
105
src/main/java/cn/wzpmc/filemanager/entities/Result.java
Normal file
105
src/main/java/cn/wzpmc/filemanager/entities/Result.java
Normal file
@ -0,0 +1,105 @@
|
||||
package cn.wzpmc.filemanager.entities;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Slf4j
|
||||
@Data
|
||||
public class Result<T> {
|
||||
private int status;
|
||||
private String msg;
|
||||
private T data;
|
||||
private long timestamp;
|
||||
protected Result() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
protected Result(int status, String msg, T data) {
|
||||
this();
|
||||
this.status = status;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
}
|
||||
protected Result(HttpStatus status) {
|
||||
this();
|
||||
this.status = status.value();
|
||||
this.msg = status.getReasonPhrase();
|
||||
}
|
||||
protected Result(HttpStatus status, String msg) {
|
||||
this(status);
|
||||
this.msg = msg;
|
||||
}
|
||||
protected Result(HttpStatus status, T data) {
|
||||
this(status);
|
||||
this.data = data;
|
||||
}
|
||||
protected Result(HttpStatus status, String msg, T data) {
|
||||
this(status, msg);
|
||||
this.data = data;
|
||||
}
|
||||
public static <T> Result<T> success() {
|
||||
return new Result<>(HttpStatus.OK);
|
||||
}
|
||||
public static <T> Result<T> success(String msg) {
|
||||
return new Result<>(HttpStatus.OK, msg);
|
||||
}
|
||||
public static <T> Result<T> success(T data) {
|
||||
return new Result<>(HttpStatus.OK, data);
|
||||
}
|
||||
public static <T> Result<T> success(String msg, T data) {
|
||||
return new Result<>(HttpStatus.OK, msg, data);
|
||||
}
|
||||
public static <T> Result<T> failed() {
|
||||
return new Result<>(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
public static <T> Result<T> failed(String msg) {
|
||||
return new Result<>(HttpStatus.FORBIDDEN, msg);
|
||||
}
|
||||
public static <T> Result<T> failed(HttpStatus status) {
|
||||
return new Result<>(status);
|
||||
}
|
||||
public static <T> Result<T> failed(HttpStatus status, String msg) {
|
||||
return new Result<>(status, msg);
|
||||
}
|
||||
public static <T> Result<T> create() {
|
||||
return new Result<>();
|
||||
}
|
||||
public Result<T> status(int status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
public Result<T> status(HttpStatus status) {
|
||||
this.status = status.value();
|
||||
this.msg = status.getReasonPhrase();
|
||||
return this;
|
||||
}
|
||||
public Result<T> msg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
public Result<T> data(T data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void writeToResponse(HttpServletResponse response){
|
||||
try(ServletOutputStream outputStream = response.getOutputStream()){
|
||||
writeToOutputStream(outputStream);
|
||||
} catch (IOException e) {
|
||||
log.trace("写出到流失败,", e);
|
||||
}
|
||||
}
|
||||
public void writeToOutputStream(OutputStream stream) throws IOException {
|
||||
stream.write(JSON.toJSONString(this).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.wzpmc.filemanager.entities.files;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class CheckChunkResponse {
|
||||
private boolean has;
|
||||
private String uploadCode;
|
||||
public static CheckChunkResponse has() {
|
||||
return new CheckChunkResponse(true, null);
|
||||
}
|
||||
public static CheckChunkResponse shouldUpload(String uploadCode) {
|
||||
return new CheckChunkResponse(false, uploadCode);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package cn.wzpmc.filemanager.entities.files;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ChunkChecked implements Serializable {
|
||||
private String fileId;
|
||||
private String hash;
|
||||
private long index;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package cn.wzpmc.filemanager.entities.files;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class ChunkReady implements Serializable {
|
||||
private long fileId;
|
||||
private long length;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package cn.wzpmc.filemanager.entities.files;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DoneUploadRequest {
|
||||
private String id;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cn.wzpmc.filemanager.entities.files;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PrepareUploadRequest {
|
||||
private String name;
|
||||
private String ext;
|
||||
private Long size;
|
||||
private int folder;
|
||||
private String fullSha1;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package cn.wzpmc.filemanager.entities.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserAuthResponse {
|
||||
private String
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package cn.wzpmc.filemanager.entities.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserLoginRequest {
|
||||
private String username;
|
||||
private String password;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cn.wzpmc.filemanager.entities.user;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.user.enums.Auth;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserRegisterRequest {
|
||||
private String username;
|
||||
private String password;
|
||||
private Auth auth;
|
||||
private String inviteCode;
|
||||
}
|
@ -1,5 +1,12 @@
|
||||
package cn.wzpmc.filemanager.entities.user.enums;
|
||||
|
||||
import com.mybatisflex.annotation.EnumValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum Auth {
|
||||
ADMIN, USER
|
||||
admin(1, "admin"), user(0, "user");
|
||||
public final int value;
|
||||
@EnumValue
|
||||
public final String name;
|
||||
}
|
@ -1,4 +1,16 @@
|
||||
package cn.wzpmc.filemanager.entities.vo;
|
||||
|
||||
public record ChunkVo(int id, String sha1, long size) {
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import lombok.Data;
|
||||
|
||||
@Table("chunk")
|
||||
@Data
|
||||
public class ChunkVo {
|
||||
@Id(keyType = KeyType.Auto)
|
||||
private int id;
|
||||
private String sha1;
|
||||
private long size;
|
||||
|
||||
}
|
@ -1,4 +1,14 @@
|
||||
package cn.wzpmc.filemanager.entities.vo;
|
||||
|
||||
public record FileChunkVo(int file, int chunk, int index) {
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Table("file_chunks")
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class FileChunkVo {
|
||||
private long file;
|
||||
private long chunk;
|
||||
private long index;
|
||||
}
|
@ -1,6 +1,26 @@
|
||||
package cn.wzpmc.filemanager.entities.vo;
|
||||
|
||||
import java.util.Date;
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
@Table("file")
|
||||
@Data
|
||||
public class FileVo {
|
||||
@Id(keyType = KeyType.Auto)
|
||||
private int id;
|
||||
private String name;
|
||||
private String ext;
|
||||
private String mime;
|
||||
private String sha1;
|
||||
private int uploader;
|
||||
private int folder;
|
||||
@Column(onInsertValue = "now()")
|
||||
private Date uploadTime;
|
||||
|
||||
public record FileVo(int id, String name, String ext, String mime, String sha1, int uploader, int folder, Date uploadTime) {
|
||||
}
|
@ -1,6 +1,22 @@
|
||||
package cn.wzpmc.filemanager.entities.vo;
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public record FolderVo(int id, String name, int parent, int creator, Date createTime) {
|
||||
@Table("folder")
|
||||
@Data
|
||||
public class FolderVo {
|
||||
@Id(keyType = KeyType.Auto)
|
||||
private int id;
|
||||
private String name;
|
||||
private int parent;
|
||||
private int creator;
|
||||
@Column(onInsertValue = "now()")
|
||||
private Date createTime;
|
||||
|
||||
}
|
@ -1,8 +1,19 @@
|
||||
package cn.wzpmc.filemanager.entities.vo;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.statistics.enums.Actions;
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public record StatisticsVo(int actor, Actions action, String params, Date time) {
|
||||
@Table("statistics")
|
||||
@Data
|
||||
public class StatisticsVo {
|
||||
private int actor;
|
||||
private Actions action;
|
||||
private String params;
|
||||
@Column(onInsertValue = "now()")
|
||||
private Date time;
|
||||
|
||||
}
|
@ -1,6 +1,27 @@
|
||||
package cn.wzpmc.filemanager.entities.vo;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.user.enums.Auth;
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Table("user")
|
||||
@Data
|
||||
public class UserVo {
|
||||
@Id(keyType = KeyType.Auto)
|
||||
private int id;
|
||||
private String name;
|
||||
private String password;
|
||||
private Auth auth;
|
||||
@Column(isLogicDelete = true, onInsertValue = "0")
|
||||
private boolean banned;
|
||||
public UserVo(String name, String password, Auth auth) {
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
this.auth = auth;
|
||||
}
|
||||
|
||||
public record UserVo(int id, String name, String password, Auth auth, boolean banned) {
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.wzpmc.filemanager.exceptions;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.Result;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class AuthorizationException extends RuntimeException {
|
||||
private final Result<Void> result;
|
||||
public AuthorizationException(Result<Void> result) {
|
||||
super(result.getMsg());
|
||||
this.result = result;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package cn.wzpmc.filemanager.mapper;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.vo.ChunkVo;
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
|
||||
public interface ChunkMapper extends BaseMapper<ChunkVo> {
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package cn.wzpmc.filemanager.mapper;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.vo.FileChunkVo;
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
|
||||
public interface FileChunksMapper extends BaseMapper<FileChunkVo> {
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package cn.wzpmc.filemanager.mapper;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.vo.FileVo;
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
|
||||
public interface FileMapper extends BaseMapper<FileVo> {
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package cn.wzpmc.filemanager.mapper;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.vo.FolderVo;
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
|
||||
public interface FolderMapper extends BaseMapper<FolderVo> {
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package cn.wzpmc.filemanager.mapper;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.vo.UserVo;
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
|
||||
public interface UserMapper extends BaseMapper<UserVo> {
|
||||
}
|
166
src/main/java/cn/wzpmc/filemanager/service/FileService.java
Normal file
166
src/main/java/cn/wzpmc/filemanager/service/FileService.java
Normal file
@ -0,0 +1,166 @@
|
||||
package cn.wzpmc.filemanager.service;
|
||||
|
||||
import cn.wzpmc.filemanager.config.FileManagerProperties;
|
||||
import cn.wzpmc.filemanager.entities.Result;
|
||||
import cn.wzpmc.filemanager.entities.files.*;
|
||||
import cn.wzpmc.filemanager.entities.vo.ChunkVo;
|
||||
import cn.wzpmc.filemanager.entities.vo.FileChunkVo;
|
||||
import cn.wzpmc.filemanager.entities.vo.FileVo;
|
||||
import cn.wzpmc.filemanager.entities.vo.UserVo;
|
||||
import cn.wzpmc.filemanager.mapper.ChunkMapper;
|
||||
import cn.wzpmc.filemanager.mapper.FileChunksMapper;
|
||||
import cn.wzpmc.filemanager.mapper.FileMapper;
|
||||
import cn.wzpmc.filemanager.mapper.FolderMapper;
|
||||
import cn.wzpmc.filemanager.utils.RandomUtils;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.wzpmc.filemanager.entities.vo.table.ChunkVoTableDef.CHUNK_VO;
|
||||
import static cn.wzpmc.filemanager.entities.vo.table.FileChunkVoTableDef.FILE_CHUNK_VO;
|
||||
import static cn.wzpmc.filemanager.entities.vo.table.FileVoTableDef.FILE_VO;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FileService {
|
||||
private final RedisTemplate<String, ChunkReady> uploadMapper;
|
||||
private final RedisTemplate<String, ChunkChecked> chunkUploadMapper;
|
||||
private final FileMapper fileMapper;
|
||||
private final ChunkMapper chunkMapper;
|
||||
private final FileChunksMapper fileChunksMapper;
|
||||
private final FolderMapper folderMapper;
|
||||
private final RandomUtils randomUtils;
|
||||
private final FileManagerProperties properties;
|
||||
private static final String UPLOAD_FILE_PREPARE_HEAD = "UPLOAD_";
|
||||
private static final String CHUNK_PREPARE_HEAD = "UPLOAD_CHUNK_";
|
||||
public Result<String> prepareUploadChunks(PrepareUploadRequest prepareUploadRequest, UserVo user) {
|
||||
String name = prepareUploadRequest.getName();
|
||||
String ext = prepareUploadRequest.getExt();
|
||||
int folder = prepareUploadRequest.getFolder();
|
||||
if (this.fileMapper.selectCountByCondition(FILE_VO.NAME.eq(name).and(FILE_VO.EXT.eq(ext)).and(FILE_VO.FOLDER.eq(folder))) > 0) {
|
||||
return Result.failed(HttpStatus.CONFLICT, "文件已存在!");
|
||||
}
|
||||
String fullSha1 = prepareUploadRequest.getFullSha1();
|
||||
FileVo otherSameFile = this.fileMapper.selectOneByCondition(FILE_VO.SHA1.eq(fullSha1));
|
||||
ChunkReady chunkReady = new ChunkReady();
|
||||
FileVo fileVo = new FileVo();
|
||||
fileVo.setUploader(user.getId());
|
||||
fileVo.setName(name);
|
||||
fileVo.setExt(ext);
|
||||
fileVo.setFolder(folder);
|
||||
fileVo.setSha1(fullSha1);
|
||||
this.fileMapper.insert(fileVo);
|
||||
int fid = fileVo.getId();
|
||||
if (otherSameFile != null) {
|
||||
int id = otherSameFile.getId();
|
||||
List<FileChunkVo> fileChunkVos = this.fileChunksMapper.selectListByCondition(FILE_CHUNK_VO.FILE.eq(id));
|
||||
for (FileChunkVo fileChunkVo : fileChunkVos) {
|
||||
fileChunkVo.setFile(fid);
|
||||
this.fileChunksMapper.insert(fileChunkVo);
|
||||
}
|
||||
return Result.failed(HttpStatus.FOUND, "后台存在相同文件,无需上传!");
|
||||
}
|
||||
String uploadId = this.randomUtils.generatorRandomString(40);
|
||||
chunkReady.setFileId(fid);
|
||||
chunkReady.setLength(prepareUploadRequest.getSize());
|
||||
uploadMapper.opsForValue().set(UPLOAD_FILE_PREPARE_HEAD + uploadId, chunkReady);
|
||||
return Result.success("成功", uploadId);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Transactional
|
||||
public Result<Void> uploadChunk(MultipartFile file, String id) {
|
||||
ValueOperations<String, ChunkChecked> chunkOps = chunkUploadMapper.opsForValue();
|
||||
ChunkChecked chunkData = chunkOps.getAndDelete(CHUNK_PREPARE_HEAD + id);
|
||||
if (chunkData == null) {
|
||||
return Result.failed(HttpStatus.NOT_FOUND, "未知的文件块");
|
||||
}
|
||||
long size = file.getSize();
|
||||
if (size > 64 * 1024 * 1024) {
|
||||
return Result.failed(HttpStatus.PAYLOAD_TOO_LARGE, "文件块不应大于64MB");
|
||||
}
|
||||
byte[] bytes = file.getBytes();
|
||||
String s = DigestUtils.sha1Hex(bytes);
|
||||
if (!s.equals(chunkData.getHash())) {
|
||||
return Result.failed(HttpStatus.CONFLICT, "文件块内容错误!");
|
||||
}
|
||||
ChunkVo chunkVo = new ChunkVo();
|
||||
chunkVo.setSize(size);
|
||||
chunkVo.setSha1(s);
|
||||
String hashHead = s.substring(0, 2);
|
||||
File hashHeadFolder = new File(properties.getSavePath(), hashHead);
|
||||
if (!hashHeadFolder.exists()) {
|
||||
if (!hashHeadFolder.mkdirs()) {
|
||||
return Result.failed(HttpStatus.INTERNAL_SERVER_ERROR, "写入文件块出现错误,创建文件夹失败");
|
||||
}
|
||||
}
|
||||
File chunkFile = new File(hashHeadFolder, s);
|
||||
if (!chunkFile.createNewFile()) {
|
||||
return Result.failed(HttpStatus.INTERNAL_SERVER_ERROR, "写入文件块出现错误,无法写入块文件");
|
||||
}
|
||||
try(FileOutputStream fos = new FileOutputStream(chunkFile)) {
|
||||
fos.write(bytes);
|
||||
}
|
||||
String fileId = chunkData.getFileId();
|
||||
ChunkReady chunkReady = uploadMapper.opsForValue().get(UPLOAD_FILE_PREPARE_HEAD + fileId);
|
||||
assert chunkReady != null;
|
||||
long fileTableId = chunkReady.getFileId();
|
||||
this.chunkMapper.insert(chunkVo);
|
||||
int chunkId = chunkVo.getId();
|
||||
FileChunkVo fileChunkVo = new FileChunkVo(fileTableId, chunkId, chunkData.getIndex());
|
||||
this.fileChunksMapper.insert(fileChunkVo);
|
||||
return Result.success("成功");
|
||||
}
|
||||
|
||||
public Result<CheckChunkResponse> checkChunk(String hash, String id, long index) {
|
||||
ChunkReady chunkReady = uploadMapper.opsForValue().get(UPLOAD_FILE_PREPARE_HEAD + id);
|
||||
if (chunkReady == null) {
|
||||
return Result.failed(HttpStatus.NOT_FOUND, "未知的文件ID!");
|
||||
}
|
||||
long fileId = chunkReady.getFileId();
|
||||
ChunkVo chunkVo = chunkMapper.selectOneByCondition(CHUNK_VO.SHA1.eq(hash));
|
||||
if (chunkVo != null) {
|
||||
FileChunkVo fileChunkVo = new FileChunkVo(fileId, chunkVo.getId(), index);
|
||||
this.fileChunksMapper.insert(fileChunkVo);
|
||||
return Result.success(CheckChunkResponse.has());
|
||||
}
|
||||
ValueOperations<String, ChunkChecked> chunkOps = chunkUploadMapper.opsForValue();
|
||||
String chunkId = randomUtils.generatorRandomString(40);
|
||||
chunkOps.set(CHUNK_PREPARE_HEAD + chunkId, new ChunkChecked(id, hash, index));
|
||||
return Result.success(CheckChunkResponse.shouldUpload(chunkId));
|
||||
}
|
||||
|
||||
public Result<FileObject> doneUpload(String id) {
|
||||
ChunkReady andDelete = uploadMapper.opsForValue().getAndDelete(UPLOAD_FILE_PREPARE_HEAD + id);
|
||||
if (andDelete == null) {
|
||||
return Result.failed(HttpStatus.NOT_FOUND, "未知的文件ID");
|
||||
}
|
||||
long totalLength = andDelete.getLength();
|
||||
long l = calcFileSize(andDelete.getFileId());
|
||||
if (l != totalLength) {
|
||||
fileMapper.deleteById(andDelete.getFileId());
|
||||
return Result.failed(HttpStatus.LENGTH_REQUIRED, "应收到" + totalLength + "字节,但只收到" + l + "字节!");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
private long calcFileSize(long id) {
|
||||
List<Long> longs = this.fileChunksMapper.selectListByQueryAs(new QueryWrapper().select(CHUNK_VO.SIZE).from(FILE_CHUNK_VO).where(FILE_CHUNK_VO.FILE.eq(id)).leftJoin(CHUNK_VO).on(CHUNK_VO.ID.eq(FILE_CHUNK_VO.CHUNK)), Long.class);
|
||||
long sum = 0L;
|
||||
for (Long aLong : longs) {
|
||||
sum += aLong;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
96
src/main/java/cn/wzpmc/filemanager/service/UserService.java
Normal file
96
src/main/java/cn/wzpmc/filemanager/service/UserService.java
Normal file
@ -0,0 +1,96 @@
|
||||
package cn.wzpmc.filemanager.service;
|
||||
|
||||
import cn.wzpmc.filemanager.entities.Result;
|
||||
import cn.wzpmc.filemanager.entities.user.UserLoginRequest;
|
||||
import cn.wzpmc.filemanager.entities.user.UserRegisterRequest;
|
||||
import cn.wzpmc.filemanager.entities.user.enums.Auth;
|
||||
import cn.wzpmc.filemanager.entities.vo.UserVo;
|
||||
import cn.wzpmc.filemanager.mapper.UserMapper;
|
||||
import cn.wzpmc.filemanager.utils.JwtUtils;
|
||||
import cn.wzpmc.filemanager.utils.RandomUtils;
|
||||
import com.mybatisflex.core.query.QueryCondition;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static cn.wzpmc.filemanager.entities.vo.table.UserVoTableDef.USER_VO;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UserService {
|
||||
private final UserMapper userMapper;
|
||||
private final JwtUtils jwtUtils;
|
||||
private final StringRedisTemplate authTemplate;
|
||||
private final RandomUtils randomUtils;
|
||||
@Autowired
|
||||
public UserService(UserMapper userMapper, JwtUtils jwtUtils, StringRedisTemplate authTemplate, RandomUtils randomUtils) {
|
||||
this.userMapper = userMapper;
|
||||
this.jwtUtils = jwtUtils;
|
||||
this.authTemplate = authTemplate;
|
||||
this.randomUtils = randomUtils;
|
||||
long count = this.userMapper.selectCountByQuery(new QueryWrapper());
|
||||
if (count == 0) {
|
||||
String s = genInviteCode();
|
||||
log.info("生成了管理员密钥:{},有效期15分钟,若失效请使用控制台命令/key或重启后端重新生成!", s);
|
||||
}
|
||||
}
|
||||
public void login(UserLoginRequest request, HttpServletResponse response) {
|
||||
String username = request.getUsername();
|
||||
String password = request.getPassword();
|
||||
String sha1edPassword = DigestUtils.sha1Hex(password);
|
||||
QueryCondition findUserCondition = USER_VO.NAME.eq(username).and(USER_VO.PASSWORD.eq(sha1edPassword));
|
||||
long count = this.userMapper.selectCountByCondition(findUserCondition);
|
||||
if (count < 0) {
|
||||
Result.failed(HttpStatus.UNAUTHORIZED, "账号或密码错误").writeToResponse(response);
|
||||
return;
|
||||
}
|
||||
UserVo userVo = this.userMapper.selectOneByCondition(findUserCondition);
|
||||
String token = this.jwtUtils.createToken(userVo.getId());
|
||||
response.addHeader("Add-Authorization", token);
|
||||
Result.success("登录成功").writeToResponse(response);
|
||||
}
|
||||
public void register(UserRegisterRequest request, HttpServletResponse response) {
|
||||
String username = request.getUsername();
|
||||
String password = request.getPassword();
|
||||
Auth auth = request.getAuth();
|
||||
if (this.userMapper.selectCountByCondition(USER_VO.NAME.eq(username)) > 0) {
|
||||
Result.failed(HttpStatus.CONFLICT).msg("用户名已存在,若需要修改密码,请联系网站管理员处理").writeToResponse(response);
|
||||
return;
|
||||
}
|
||||
if (auth.equals(Auth.admin)) {
|
||||
String inviteCode = request.getInviteCode();
|
||||
ValueOperations<String, String> ops = authTemplate.opsForValue();
|
||||
String andDelete = ops.getAndDelete(inviteCode);
|
||||
if (andDelete == null) {
|
||||
Result.failed(HttpStatus.NOT_FOUND, "过期或无效的邀请码").writeToResponse(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
UserVo userVo = new UserVo(username, password, auth);
|
||||
this.userMapper.insert(userVo);
|
||||
int id = userVo.getId();
|
||||
String token = this.jwtUtils.createToken(id);
|
||||
response.addHeader("Add-Authorization", token);
|
||||
Result.success("注册成功!").writeToResponse(response);
|
||||
}
|
||||
|
||||
public Result<String> invite() {
|
||||
String s = genInviteCode();
|
||||
return Result.success("生成了一个有效期15分钟的邀请码", s);
|
||||
}
|
||||
public String genInviteCode() {
|
||||
ValueOperations<String, String> ops = authTemplate.opsForValue();
|
||||
String s = this.randomUtils.generatorRandomString(8);
|
||||
log.info("生成了新的邀请码:{}", s);
|
||||
ops.set(s, "", 15, TimeUnit.MINUTES);
|
||||
return s;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package cn.wzpmc.filemanager.utils;
|
||||
|
||||
import cn.wzpmc.filemanager.annotation.AuthorizationRequired;
|
||||
import cn.wzpmc.filemanager.exceptions.AuthorizationException;
|
||||
import jakarta.annotation.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
@Component
|
||||
public class AuthorizationArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
private final AuthorizationUtils authorizationUtils;
|
||||
@Autowired
|
||||
public AuthorizationArgumentResolver(AuthorizationUtils authorizationUtils){
|
||||
this.authorizationUtils = authorizationUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(AuthorizationRequired.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object resolveArgument(@NonNull MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, @NonNull NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws AuthorizationException {
|
||||
AuthorizationRequired parameterAnnotation = parameter.getParameterAnnotation(AuthorizationRequired.class);
|
||||
assert parameterAnnotation != null;
|
||||
return this.authorizationUtils.auth(webRequest, parameterAnnotation);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package cn.wzpmc.filemanager.utils;
|
||||
|
||||
import cn.wzpmc.filemanager.annotation.AuthorizationRequired;
|
||||
import cn.wzpmc.filemanager.exceptions.AuthorizationException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@Component
|
||||
public class AuthorizationHandlerInterceptor implements HandlerInterceptor {
|
||||
private final AuthorizationUtils authorizationUtils;
|
||||
@Autowired
|
||||
public AuthorizationHandlerInterceptor(AuthorizationUtils authorizationUtils) {
|
||||
this.authorizationUtils = authorizationUtils;
|
||||
}
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest request,@NonNull HttpServletResponse response,@NonNull Object handler) throws AuthorizationException {
|
||||
if (handler instanceof HandlerMethod method) {
|
||||
if (!method.hasMethodAnnotation(AuthorizationRequired.class)) {
|
||||
return true;
|
||||
}
|
||||
AuthorizationRequired annotation = method.getMethodAnnotation(AuthorizationRequired.class);
|
||||
return authorizationUtils.auth(request, annotation);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package cn.wzpmc.filemanager.utils;
|
||||
|
||||
import cn.wzpmc.filemanager.annotation.AuthorizationRequired;
|
||||
import cn.wzpmc.filemanager.entities.Result;
|
||||
import cn.wzpmc.filemanager.entities.user.enums.Auth;
|
||||
import cn.wzpmc.filemanager.entities.vo.UserVo;
|
||||
import cn.wzpmc.filemanager.exceptions.AuthorizationException;
|
||||
import cn.wzpmc.filemanager.mapper.UserMapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AuthorizationUtils {
|
||||
private final JwtUtils jwtUtils;
|
||||
private final UserMapper userMapper;
|
||||
@Autowired
|
||||
public AuthorizationUtils(JwtUtils jwtUtils, UserMapper userMapper) {
|
||||
this.jwtUtils = jwtUtils;
|
||||
this.userMapper = userMapper;
|
||||
}
|
||||
private UserVo auth(String header, AuthorizationRequired authorizationRequired) throws AuthorizationException {
|
||||
log.info("auth {} with token {}", authorizationRequired, header);
|
||||
if (header == null) {
|
||||
throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "未找到token"));
|
||||
}
|
||||
Auth level = authorizationRequired.level();
|
||||
Optional<Integer> user = this.jwtUtils.getUser(header);
|
||||
if (user.isEmpty()) {
|
||||
throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "token错误或已过期"));
|
||||
}
|
||||
Integer i = user.get();
|
||||
UserVo userVo = this.userMapper.selectOneById(i);
|
||||
if (userVo == null) {
|
||||
throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "用户不存在"));
|
||||
}
|
||||
Auth auth = userVo.getAuth();
|
||||
if (authorizationRequired.force()) {
|
||||
if (auth.value == level.value) {
|
||||
return userVo;
|
||||
}
|
||||
}else {
|
||||
if (auth.value >= level.value) {
|
||||
return userVo;
|
||||
}
|
||||
}
|
||||
throw new AuthorizationException(Result.failed(HttpStatus.UNAUTHORIZED, "权限不足"));
|
||||
}
|
||||
public UserVo auth(WebRequest request, AuthorizationRequired authorizationRequired) throws AuthorizationException {
|
||||
return auth(request.getHeader("Authorization"), authorizationRequired);
|
||||
}
|
||||
public boolean auth(HttpServletRequest request, AuthorizationRequired authorizationRequired) throws AuthorizationException {
|
||||
auth(request.getHeader("Authorization"), authorizationRequired);
|
||||
return true;
|
||||
}
|
||||
}
|
56
src/main/java/cn/wzpmc/filemanager/utils/JwtUtils.java
Normal file
56
src/main/java/cn/wzpmc/filemanager/utils/JwtUtils.java
Normal file
@ -0,0 +1,56 @@
|
||||
package cn.wzpmc.filemanager.utils;
|
||||
|
||||
import cn.wzpmc.filemanager.config.FileManagerProperties;
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.JWTCreator;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.interfaces.Claim;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
@Component
|
||||
@Log4j2
|
||||
public class JwtUtils {
|
||||
private final Algorithm hmacKey;
|
||||
private final RandomUtils randomUtils;
|
||||
private String generatorHmacKey(){
|
||||
return this.randomUtils.generatorRandomString(16);
|
||||
}
|
||||
@Autowired
|
||||
public JwtUtils(FileManagerProperties properties, RandomUtils randomUtils){
|
||||
this.randomUtils = randomUtils;
|
||||
String hmacKey = properties.getHmacKey();
|
||||
String key;
|
||||
if ("RANDOM".equalsIgnoreCase(hmacKey)){
|
||||
key = this.generatorHmacKey();
|
||||
log.info("Using Random Hmac Key: {}", key);
|
||||
}else{
|
||||
key = hmacKey;
|
||||
}
|
||||
this.hmacKey = Algorithm.HMAC512(key);
|
||||
}
|
||||
public String createToken(int uid){
|
||||
Calendar instance = Calendar.getInstance();
|
||||
instance.add(Calendar.HOUR,24 * 5);
|
||||
JWTCreator.Builder builder = JWT.create();
|
||||
builder.withClaim("uid", uid);
|
||||
builder.withExpiresAt(instance.getTime());
|
||||
return builder.sign(this.hmacKey);
|
||||
}
|
||||
public Optional<Integer> getUser(String token){
|
||||
DecodedJWT verify;
|
||||
try {
|
||||
verify = JWT.require(this.hmacKey).build().verify(token);
|
||||
}catch (Exception e){
|
||||
return Optional.empty();
|
||||
}
|
||||
Claim uid = verify.getClaim("uid");
|
||||
return Optional.of(uid.asInt());
|
||||
}
|
||||
}
|
19
src/main/java/cn/wzpmc/filemanager/utils/RandomUtils.java
Normal file
19
src/main/java/cn/wzpmc/filemanager/utils/RandomUtils.java
Normal file
@ -0,0 +1,19 @@
|
||||
package cn.wzpmc.filemanager.utils;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Component
|
||||
@NoArgsConstructor
|
||||
public class RandomUtils {
|
||||
public String generatorRandomString(int length) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int c = new Random().nextInt(33, 126);
|
||||
builder.append((char) c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
@ -13,6 +13,11 @@ spring:
|
||||
host: "server.wzpmc.cn"
|
||||
database: 3
|
||||
password: "MyCraftAdmin123"
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 100GB
|
||||
max-request-size: 100GB
|
||||
wzp:
|
||||
filemanager:
|
||||
save-path: "./file"
|
||||
save-path: "./file"
|
||||
hmac-key: "V(LWJ6D5*4%,Hk{1"
|
Loading…
x
Reference in New Issue
Block a user