From 62ded00cb3430cadb705a18f6f95de1b01c572c2 Mon Sep 17 00:00:00 2001 From: merlin Date: Sun, 2 Nov 2025 00:03:16 +0800 Subject: [PATCH] refactor: refactoring code; update encryption algorithm; add build ci; --- .gitea/workflows/main.yaml | 33 ++++++ .gitea/workflows/tag.yaml | 32 ++++++ Dockerfile | 24 ++++ .../controller/AdminController.java | 103 ++++++++++++++++++ .../controller/BasicController.java | 8 +- .../controller/LoginController.java | 86 +++++++++------ .../controller/MailController.java | 2 - .../xin/merlin/myblog_server/entity/News.java | 3 +- .../security/JWTAuthenticationFilter.java | 2 +- .../security/PasswordConfig.java | 17 +++ .../myblog_server/service/LoginService.java | 59 ++++++++++ .../service/impl/UserServiceImpl.java | 1 - .../merlin/myblog_server/utils/JwtUtil.java | 1 + .../myblog_server/utils/SHA256Util.java | 5 +- src/main/resources/application.yml | 7 -- 15 files changed, 330 insertions(+), 53 deletions(-) create mode 100644 .gitea/workflows/main.yaml create mode 100644 .gitea/workflows/tag.yaml create mode 100644 Dockerfile create mode 100644 src/main/java/xin/merlin/myblog_server/controller/AdminController.java create mode 100644 src/main/java/xin/merlin/myblog_server/security/PasswordConfig.java create mode 100644 src/main/java/xin/merlin/myblog_server/service/LoginService.java diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml new file mode 100644 index 0000000..c374c4e --- /dev/null +++ b/.gitea/workflows/main.yaml @@ -0,0 +1,33 @@ +name: Docker Image CI +on: + push: + branches: + - main + +jobs: + build: + runs-on: gitea-official-runner + container: + image: harbor.merlin.xin/testing/merlin/builder:v0.0.0 + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: docker login + env: + HARBOR_USERNAME: ${{ secrets.HARBOR_ROBOT }} + HARBOR_PASSWORD: ${{ secrets.HARBOR_ROBOT_SECRET }} + HARBOR_URL: ${{ vars.HARBOR_URL }} + run: docker login ${HARBOR_URL} -u ${HARBOR_USERNAME} -p ${HARBOR_PASSWORD} + - name: Build and push Docker images + env: + HARBOR_URL: ${{ vars.HARBOR_URL }} + TAG: ${{ github.sha }} + REPOSITORY: ${{ github.repository }} + run: | + ROOT_DIR=$(pwd) + IMAGE_NAME="${HARBOR_URL}/testing/$REPOSITORY:${TAG}" + echo "Building image: ${IMAGE_NAME}" + docker build -t ${IMAGE_NAME} . + echo "Pushing image: ${IMAGE_NAME}" + docker push ${IMAGE_NAME} + echo "Successfully pushed: ${IMAGE_NAME}" diff --git a/.gitea/workflows/tag.yaml b/.gitea/workflows/tag.yaml new file mode 100644 index 0000000..b54e102 --- /dev/null +++ b/.gitea/workflows/tag.yaml @@ -0,0 +1,32 @@ +name: Docker Image CI +on: + push: + tags: + - '*' + +jobs: + build: + runs-on: gitea-official-runner + container: + image: harbor.merlin.xin/testing/merlin/builder:v0.0.0 + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: docker login + env: + HARBOR_USERNAME: ${{ secrets.HARBOR_ROBOT }} + HARBOR_PASSWORD: ${{ secrets.HARBOR_ROBOT_SECRET }} + HARBOR_URL: ${{ vars.HARBOR_URL }} + run: docker login ${HARBOR_URL} -u ${HARBOR_USERNAME} -p ${HARBOR_PASSWORD} + - name: Build and push Docker images + env: + HARBOR_URL: ${{ vars.HARBOR_URL }} + REPOSITORY: ${{ github.repository }} + run: | + ROOT_DIR=$(pwd) + IMAGE_NAME="${HARBOR_URL}/release/$REPOSITORY:$GITHUB_REF_NAME" + echo "Building image: ${IMAGE_NAME}" + docker build -t ${IMAGE_NAME} . + echo "Pushing image: ${IMAGE_NAME}" + docker push ${IMAGE_NAME} + echo "Successfully pushed: ${IMAGE_NAME}" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e759410 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# ===== build stage ===== +FROM harbor.merlin.xin/mirrors/docker.io/library/maven:3.9.6-eclipse-temurin-17 AS builder + +WORKDIR /app +COPY pom.xml . +RUN --mount=type=cache,target=/root/.m2 mvn -B -q dependency:go-offline + +COPY . . +RUN --mount=type=cache,target=/root/.m2 mvn -B -q package -DskipTests + + +# ===== runtime stage ===== +FROM harbor.merlin.xin/mirrors/docker.io/library/eclipse-temurin:17-jre-alpine + +WORKDIR /app +COPY --from=builder /app/target/*.jar app.jar + +RUN mkdir uploads/photo + +# 非 root 用户运行 +RUN addgroup -S spring && adduser -S spring -G spring +USER spring + +ENTRYPOINT ["java","-jar","/app/app.jar"] diff --git a/src/main/java/xin/merlin/myblog_server/controller/AdminController.java b/src/main/java/xin/merlin/myblog_server/controller/AdminController.java new file mode 100644 index 0000000..054f19d --- /dev/null +++ b/src/main/java/xin/merlin/myblog_server/controller/AdminController.java @@ -0,0 +1,103 @@ +package xin.merlin.myblog_server.controller; + + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import xin.merlin.myblog_server.entity.Article; +import xin.merlin.myblog_server.entity.Comment; +import xin.merlin.myblog_server.entity.News; +import xin.merlin.myblog_server.entity.User; +import xin.merlin.myblog_server.service.impl.ArticleServiceImpl; +import xin.merlin.myblog_server.service.impl.CommentServiceImpl; +import xin.merlin.myblog_server.service.impl.NewsServiceImpl; +import xin.merlin.myblog_server.utils.JwtUtil; +import xin.merlin.myblog_server.utils.RequestBack; +import xin.merlin.myblog_server.utils.enums.ResultCode; + +@RestController +@RequestMapping("/admin") +public class AdminController { + + + @Autowired + private JwtUtil jwtUtil; + + @Autowired + private NewsServiceImpl newsService; + + @Autowired + private ArticleServiceImpl articleService; + + @Autowired + private CommentServiceImpl commentService; + + //编辑,新增,删除新闻 + @PostMapping("/update/news") + RequestBack editNews(@RequestBody News news, @RequestHeader("Authorization")String token) { + if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND); + newsService.updateById(news); + return RequestBack.success(ResultCode.SUCCESS); + } + + @PostMapping("/add/news") + RequestBack addNews(@RequestBody News news, @RequestHeader("Authorization")String token) { + if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND); + newsService.save(news); + return RequestBack.success(ResultCode.SUCCESS); + } + + @PostMapping("/delete/news") + RequestBack deleteNews(@RequestBody News news,@RequestHeader("Authorization")String token) { + if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND); + newsService.removeById(news.getId()); + return RequestBack.success(ResultCode.SUCCESS); + } + + //编辑,新增,删除文章 + @PostMapping("/update/article") + RequestBack editArticle(@RequestBody Article article, @RequestHeader("Authorization")String token) { + if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND); + articleService.updateById(article); + return RequestBack.success(ResultCode.SUCCESS); + } + + @PostMapping("/add/article") + RequestBack addArticle(@RequestBody Article article, @RequestHeader("Authorization")String token) { + if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND); + articleService.save(article); + return RequestBack.success(ResultCode.SUCCESS); + } + + @PostMapping("/delete/article") + RequestBack deleteArticle(@RequestBody Article article, @RequestHeader("Authorization")String token) { + if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND); + articleService.removeById(article.getId()); + return RequestBack.success(ResultCode.SUCCESS); + + } + + //获取、删除评论 + @GetMapping("/get/comments") + RequestBack getComments(@RequestParam Integer current,@RequestParam Integer size,@RequestHeader("Authorization")String token) { + if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND); + Page page = new Page<>(current,size); + + return RequestBack.success(ResultCode.SUCCESS,commentService.page(page)); + } + + @PostMapping("/delete/comment") + RequestBack addArticle(@RequestBody Comment comment, @RequestHeader("Authorization")String token) { + if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND); + commentService.removeById(comment.getId()); + return RequestBack.success(ResultCode.SUCCESS); + } + + //获取,删除用户 + @PostMapping("/get/users") + RequestBack getUsers(@RequestBody User user , @RequestHeader("Authorization")String token) { + if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND); + commentService.removeById(user.getId()); + return RequestBack.success(ResultCode.SUCCESS); + } +} diff --git a/src/main/java/xin/merlin/myblog_server/controller/BasicController.java b/src/main/java/xin/merlin/myblog_server/controller/BasicController.java index f9d6daf..a877758 100644 --- a/src/main/java/xin/merlin/myblog_server/controller/BasicController.java +++ b/src/main/java/xin/merlin/myblog_server/controller/BasicController.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import xin.merlin.myblog_server.entity.Article; import xin.merlin.myblog_server.entity.Comment; import xin.merlin.myblog_server.entity.News; import xin.merlin.myblog_server.service.impl.*; @@ -49,7 +50,12 @@ public class BasicController { public RequestBack getArticle(@PathVariable Integer a_id) { return RequestBack.success(ResultCode.SUCCESS,articleService.getById(a_id)); } - // 参与项目 + // 获取文章 + @GetMapping("/get/articles") + public RequestBack getArticles(@RequestParam Integer current,@RequestParam Integer size) { + Page
page = new Page<>(current,size); + return RequestBack.success(ResultCode.SUCCESS,articleService.page(page)); + } // 发表评论 @PostMapping("/publish/comment") public RequestBack publishComment(@RequestBody Comment comment) { diff --git a/src/main/java/xin/merlin/myblog_server/controller/LoginController.java b/src/main/java/xin/merlin/myblog_server/controller/LoginController.java index 4ff829e..d28fc7d 100644 --- a/src/main/java/xin/merlin/myblog_server/controller/LoginController.java +++ b/src/main/java/xin/merlin/myblog_server/controller/LoginController.java @@ -10,10 +10,10 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import xin.merlin.myblog_server.config.CustomUserDetails; import xin.merlin.myblog_server.config.LoginDetails; import xin.merlin.myblog_server.entity.User; import xin.merlin.myblog_server.service.CacheService; +import xin.merlin.myblog_server.service.LoginService; import xin.merlin.myblog_server.service.impl.UserServiceImpl; import xin.merlin.myblog_server.utils.JwtUtil; import xin.merlin.myblog_server.utils.RequestBack; @@ -25,7 +25,6 @@ import java.util.Map; @RestController @RequiredArgsConstructor public class LoginController { - @Autowired private LoginDetails loginDetails; @@ -38,8 +37,9 @@ public class LoginController { @Autowired private JwtUtil jwtUtil; - @Autowired - private HttpServletRequest request; + private final LoginService loginService; + + private final HttpServletRequest request; @Autowired private CacheService cacheService; @@ -48,27 +48,34 @@ public class LoginController { @PostMapping("/login") public RequestBack login(@RequestBody User user) { try { - String ip = request.getRemoteAddr(); - user.setIp(ip); - - CustomUserDetails userDetails = loginDetails - .loadUserByUsername(user.getAccount()); - user.setPassword(sha256Util - .encryptPassword(user.getPassword())); - - if (userDetails == null) { - return RequestBack.success(ResultCode.USER_NOT_FOUND); - } - //System.out.println(account.getU_password()); - // 验证密码 - if(!user.getPassword().equals(userDetails.getPassword())) - return RequestBack.success(ResultCode.USER_PASSWORD_ERROR,null); - - //System.out.println(userDetails.getU_id()); - // 生成token - String token = jwtUtil.generateToken(user.getAccount(),userDetails.getU_id()); - - return RequestBack.success(ResultCode.SUCCESS, Map.of("token",token,"token_type","Bearer","role","User")); +// CustomUserDetails userDetails = loginDetails +// .loadUserByUsername(user.getAccount()); +// user.setPassword(sha256Util +// .encryptPassword(user.getPassword())); +// +// if (userDetails == null) { +// return RequestBack.success(ResultCode.USER_NOT_FOUND); +// } +// //System.out.println(account.getU_password()); +// // 验证密码 +// if(!user.getPassword().equals(userDetails.getPassword())) +// return RequestBack.success(ResultCode.USER_PASSWORD_ERROR,null); +// +// //System.out.println(userDetails.getU_id()); +// // 生成token +// String token = jwtUtil.generateToken(user.getAccount(),userDetails.getU_id()); +// +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// queryWrapper.eq("account", user.getAccount()); +// User userinfo = userServiceImpl.getOne(queryWrapper); +// userinfo.setIp(request.getRemoteAddr()); +// userServiceImpl.updateById(userinfo); +// userinfo.setPassword(null); +// +// return RequestBack.success(ResultCode.SUCCESS, Map.of("token",token,"token_type","Bearer","role",user.getAccount().equals("admin")?"admin":"User","userInfo",userinfo)); + Map result = loginService.login(user, request.getRemoteAddr()); + if(result == null) return RequestBack.success(ResultCode.USER_PASSWORD_ERROR); + return RequestBack.success(ResultCode.SUCCESS,result); } catch (UsernameNotFoundException e) { System.out.println(e.getMessage()); return RequestBack.fail(ResultCode.SERVER_ERROR); @@ -80,19 +87,26 @@ public class LoginController { public RequestBack register(@RequestBody User user, @RequestParam String c_id) { if(c_id == null || cacheService.getWaitingList().getIfPresent(c_id)==null) return RequestBack.fail(ResultCode.USER_VERIFICATION_ERROR); try { - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("account", user.getAccount()); - if(userServiceImpl.exists(queryWrapper)) return RequestBack.success(ResultCode.USER_EXIST,null); - - // 注册信息初始化 +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// queryWrapper.eq("account", user.getAccount()); +// if(userServiceImpl.exists(queryWrapper)) return RequestBack.success(ResultCode.USER_EXIST,null); +// +// // 注册信息初始化 +// user.setIp(request.getRemoteAddr()); +// user.setPassword(sha256Util.encryptPassword(user.getPassword())); +// +// // 注册 +// userServiceImpl.save(user); +// cacheService.getWaitingList().invalidate(c_id); +// +// return RequestBack.success(ResultCode.SUCCESS,null); user.setIp(request.getRemoteAddr()); - user.setPassword(sha256Util.encryptPassword(user.getPassword())); + RequestBack result = loginService.register(user); + if(result.getCode() == 200) { + cacheService.getWaitingList().invalidate(c_id); + } + return result; - // 注册 - userServiceImpl.save(user); - cacheService.getWaitingList().invalidate(c_id); - - return RequestBack.success(ResultCode.SUCCESS,null); } catch (Exception e) { System.out.println(e.getMessage()); return RequestBack.fail(ResultCode.SERVER_ERROR); diff --git a/src/main/java/xin/merlin/myblog_server/controller/MailController.java b/src/main/java/xin/merlin/myblog_server/controller/MailController.java index 9903259..55a9216 100644 --- a/src/main/java/xin/merlin/myblog_server/controller/MailController.java +++ b/src/main/java/xin/merlin/myblog_server/controller/MailController.java @@ -68,8 +68,6 @@ public class MailController { @PostMapping("/verifycode") RequestBack verifyCode(@RequestBody Code code) { //测试验证码 - if (code.getCode().equals("666666")) return RequestBack.success(ResultCode.SUCCESS); - String id=code.getC_id(); if(codeFailCount.getIfPresent(id) == null) codeFailCount.put(id, 1); diff --git a/src/main/java/xin/merlin/myblog_server/entity/News.java b/src/main/java/xin/merlin/myblog_server/entity/News.java index 0725bbd..bb4ea11 100644 --- a/src/main/java/xin/merlin/myblog_server/entity/News.java +++ b/src/main/java/xin/merlin/myblog_server/entity/News.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.time.LocalDateTime; +import java.util.List; @Data @TableName("news") @@ -14,6 +15,6 @@ public class News { private String title; private String content; private String published; - private Integer[] related; + private String related; } diff --git a/src/main/java/xin/merlin/myblog_server/security/JWTAuthenticationFilter.java b/src/main/java/xin/merlin/myblog_server/security/JWTAuthenticationFilter.java index 6a4ef79..f5af2d6 100644 --- a/src/main/java/xin/merlin/myblog_server/security/JWTAuthenticationFilter.java +++ b/src/main/java/xin/merlin/myblog_server/security/JWTAuthenticationFilter.java @@ -30,7 +30,7 @@ public class JWTAuthenticationFilter extends OncePerRequestFilter { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { - String token = authHeader.substring(7); + String token = authHeader.substring(7).trim(); try { if (!jwtUtil.isTokenExpired(token)) { diff --git a/src/main/java/xin/merlin/myblog_server/security/PasswordConfig.java b/src/main/java/xin/merlin/myblog_server/security/PasswordConfig.java new file mode 100644 index 0000000..5e4e6b8 --- /dev/null +++ b/src/main/java/xin/merlin/myblog_server/security/PasswordConfig.java @@ -0,0 +1,17 @@ +package xin.merlin.myblog_server.security; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + +} diff --git a/src/main/java/xin/merlin/myblog_server/service/LoginService.java b/src/main/java/xin/merlin/myblog_server/service/LoginService.java new file mode 100644 index 0000000..7cb1ecc --- /dev/null +++ b/src/main/java/xin/merlin/myblog_server/service/LoginService.java @@ -0,0 +1,59 @@ +package xin.merlin.myblog_server.service; + + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import xin.merlin.myblog_server.entity.User; +import xin.merlin.myblog_server.mapper.UserMapper; +import xin.merlin.myblog_server.utils.JwtUtil; +import xin.merlin.myblog_server.utils.RequestBack; +import xin.merlin.myblog_server.utils.enums.ResultCode; + +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class LoginService { + + private final PasswordEncoder passwordEncoder; + + private final UserMapper userMapper; + + private final JwtUtil jwtUtil; + + public Map login(User user, String ip){ + try { + User u = userMapper.selectOne(new QueryWrapper().eq("account", user.getAccount())); + + if(passwordEncoder.matches(user.getPassword(),u.getPassword())){ + String token = jwtUtil.generateToken(u.getAccount(),u.getId()); + u.setIp(ip); + userMapper.updateById(u); + + u.setPassword(null); + return Map.of("token",token,"token_type","Bearer","role",u.getAccount().equals("admin")?"admin":"User","userInfo",u); + } + else + return null; + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + + public RequestBack register(User user){ + try { + if (userMapper.exists(new QueryWrapper().eq("account", user.getAccount()))) { + return RequestBack.success(ResultCode.USER_EXIST); + } + user.setPassword(passwordEncoder.encode(user.getPassword())); + userMapper.insert(user); + return RequestBack.success(ResultCode.SUCCESS); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/xin/merlin/myblog_server/service/impl/UserServiceImpl.java b/src/main/java/xin/merlin/myblog_server/service/impl/UserServiceImpl.java index 819a411..2b8ff29 100644 --- a/src/main/java/xin/merlin/myblog_server/service/impl/UserServiceImpl.java +++ b/src/main/java/xin/merlin/myblog_server/service/impl/UserServiceImpl.java @@ -2,7 +2,6 @@ package xin.merlin.myblog_server.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import xin.merlin.myblog_server.entity.User; import xin.merlin.myblog_server.mapper.UserMapper; diff --git a/src/main/java/xin/merlin/myblog_server/utils/JwtUtil.java b/src/main/java/xin/merlin/myblog_server/utils/JwtUtil.java index f01ddaa..2e0a4a2 100644 --- a/src/main/java/xin/merlin/myblog_server/utils/JwtUtil.java +++ b/src/main/java/xin/merlin/myblog_server/utils/JwtUtil.java @@ -44,6 +44,7 @@ public class JwtUtil { return Jwts.builder() .subject(uAccount) .claim("id", uId) + .claim("account", uAccount) .id(UUID.randomUUID().toString()) .issuedAt(now) .expiration(expireDate) diff --git a/src/main/java/xin/merlin/myblog_server/utils/SHA256Util.java b/src/main/java/xin/merlin/myblog_server/utils/SHA256Util.java index de86e22..2de9142 100644 --- a/src/main/java/xin/merlin/myblog_server/utils/SHA256Util.java +++ b/src/main/java/xin/merlin/myblog_server/utils/SHA256Util.java @@ -45,12 +45,9 @@ public class SHA256Util { * @param password 用户输入的密码 * @return 加密后的密码哈希值 */ - @Value("${jwt.salt}") - private String salt; public String encryptPassword(String password) { - String s = encryptSHA256(salt); // 将盐值与密码拼接后进行SHA-256加密 - return encryptSHA256(s +s + password + s); + return encryptSHA256( password); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 769299f..0bca0d5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -12,14 +12,7 @@ jwt: issuer: blogAdmin subject: Interesting expire: 604800 - salt: sdjhjksdzkfhjkdzs -file: - image-dir: C:/Temp/uploads/images #图片储存目录 - avatar-dir: C:/Temp/uploads/avatars # 头像存储目录(Windows 环境) - -# avatar-dir: /home/blog/uploads/avatars # 头像储存目录(Linux 环境) -# image-dir: /home/blog/uploads/image spring: servlet: