diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/config/WebConfig.java b/src/main/java/uk/ac/cf/spring/demo/sports/config/WebConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..4bd1f4302b749e201d9d8f90819e33f9ae8043ec
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/config/WebConfig.java
@@ -0,0 +1,14 @@
+package uk.ac.cf.spring.demo.sports.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("/uploads/**")
+                .addResourceLocations("file:./uploads/"); // 指定静态资源的实际路径
+    }
+}
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/images/Image.java b/src/main/java/uk/ac/cf/spring/demo/sports/images/Image.java
new file mode 100644
index 0000000000000000000000000000000000000000..b51b609b91fcce93d91ffd7c3d89372f01548955
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/images/Image.java
@@ -0,0 +1,16 @@
+package uk.ac.cf.spring.demo.sports.images;
+
+import java.sql.Timestamp;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+@Data
+@NoArgsConstructor
+
+public class Image {
+    private Integer id;
+    private String url;
+    private Timestamp uploadedAt;
+
+
+}
+
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageController.java b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageController.java
new file mode 100644
index 0000000000000000000000000000000000000000..c137de20530d933d88f72b4ff075e905d877e7ff
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageController.java
@@ -0,0 +1,39 @@
+package uk.ac.cf.spring.demo.sports.images;
+import java.io.IOException;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestController
+@RequestMapping("/api/images")
+public class ImageController {
+    private final ImageService imageService;
+
+    @Autowired
+    public ImageController(ImageService imageService) {
+        this.imageService = imageService;
+    }
+
+    @GetMapping
+    public List<Image> getImages() {
+        List<Image> images = imageService.getAllImages();
+        System.out.println("Returned JSON data: " + images);
+        return imageService.getAllImages();
+    }
+
+    @PostMapping
+    public ResponseEntity<Void> uploadImage(@RequestParam("image") MultipartFile file) {
+        try {
+            imageService.saveImage(file);
+            List<Image> images = imageService.getAllImages();
+            System.out.println("Returned JSON data: " + images);
+            return ResponseEntity.ok().build();
+        } catch (IOException e) {
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
+        }
+    }
+}
+
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageRepository.java b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..3feedb21d13c98ddd40abc5592b4e2436dc5e50c
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageRepository.java
@@ -0,0 +1,42 @@
+package uk.ac.cf.spring.demo.sports.images;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+@Repository
+public class ImageRepository {
+    private final JdbcTemplate jdbcTemplate;
+
+    @Autowired
+    public ImageRepository(JdbcTemplate jdbcTemplate) {
+        this.jdbcTemplate = jdbcTemplate;
+    }
+
+
+    public int save(String url) {
+        String sql = "INSERT INTO images (url) VALUES (?)";
+        return jdbcTemplate.update(sql, url);
+    }
+    // 获取所有图片记录
+    public List<Image> findAll() {
+        String sql = "SELECT * FROM images";
+        return jdbcTemplate.query(sql, new ImageRowMapper());
+    }
+    //映射方法
+    private static class ImageRowMapper implements RowMapper<Image> {
+        @Override
+        public Image mapRow(ResultSet rs, int rowNum) throws SQLException {
+            Image image = new Image();
+            image.setId(rs.getInt("id"));
+            image.setUrl(rs.getString("url"));
+            image.setUploadedAt(rs.getTimestamp("uploaded_at"));
+            return image;
+        }
+    }
+
+}
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageService.java b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageService.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ea853716315f773b269a6b96fb220e54dbc5f19
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageService.java
@@ -0,0 +1,11 @@
+package uk.ac.cf.spring.demo.sports.images;
+
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.List;
+
+public interface ImageService {
+    List<Image> getAllImages();
+    void saveImage(MultipartFile file) throws IOException;
+}
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageServiceImpl.java b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..bac30bb083bd67189a56cfbd86f3fd4bb8373572
--- /dev/null
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/images/ImageServiceImpl.java
@@ -0,0 +1,50 @@
+package uk.ac.cf.spring.demo.sports.images;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.UUID;
+
+@Service
+public class ImageServiceImpl implements ImageService {
+
+    private final ImageRepository imageRepository;
+
+    @Value("${image.upload.dir}")
+    private String uploadDir;
+
+    @Autowired
+    public ImageServiceImpl(ImageRepository imageRepository) {
+        this.imageRepository = imageRepository;
+    }
+
+    @Override
+    public List<Image> getAllImages() {
+        return imageRepository.findAll();
+    }
+
+    @Override
+    public void saveImage(MultipartFile file) throws IOException {
+        // 生成唯一文件名
+        String fileName = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
+        Path filePath = Paths.get(uploadDir, fileName);
+
+        // 确保目录存在
+        Files.createDirectories(filePath.getParent());
+
+        // 将文件写入目标目录
+        Files.write(filePath, file.getBytes());
+
+        // 保存图片路径到数据库
+        String url = "/uploads/" + fileName;
+        imageRepository.save(url);
+    }
+
+}
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java
index 10a8e80dedb69bb13ea24da5ff55cbf1fa1189ff..e401b0ff6a5f35c377ab513f4f56e65afceddf82 100644
--- a/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java
@@ -11,72 +11,52 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
 import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 
-import static org.springframework.security.config.Customizer.withDefaults;
-
 @Configuration
 @EnableWebSecurity
 public class SecurityConfig {
 
     public static final String[] ENDPOINTS_WHITELIST = {
             "/pics/**",
-            "/css/**",          // .CSS Style Files
-            "/js/**",           // .JS Files
+            "/css/**",          // Style Files
+            "/js/**",           // JS Files
             "/images/**",       // images
             "/",                // root path
             "/match",           // match data path
             "/match/**",
-            "/users",           // user data path
-            "/users/**",
             "/api/**",          // API
             "/html/**",         // static HTML
-            "/html/register.html",  // registration path
+            "/html/register.html",        // register
             "/html/login.html",  // login
             "/html/table-tennisrules.html",
+            "/html/login.html" , // login
             "/rankings",          //ranking data path
             "/rankings/**",
-            "/html/table-tennisrules.html"
-    };
+            "/uploads",            //images
+            "/uploads/**",
 
-    public static final String[] ADMIN_WHITELIST = {
-            "/admin/**",
     };
 
     @Bean
     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         http
-                // Configure access rights for paths
                 .authorizeHttpRequests(request -> request
-                        .requestMatchers(ENDPOINTS_WHITELIST).permitAll() // Allow whitelisted pages without authentication
-                        .requestMatchers("/users/current").permitAll()
-                        .requestMatchers("/html/backGroundMatch.html").hasRole("ADMIN") // Only users with the ADMIN role are allowed to access
-                        .anyRequest().authenticated() // Other paths require authentication
+                        .requestMatchers(ENDPOINTS_WHITELIST).permitAll() // 允许白名单页面不需要认证
+                        .anyRequest().authenticated() // 其他页面需要认证
                 )
-
-                // Disable CSRF (can be enabled on demand)
                 .csrf().disable()
-
-                // Forms Login Configuration
+//                .formLogin(form -> form
+//                        .loginPage("/login").permitAll() // 定义自定义登录页面
+//                )
                 .formLogin(form -> form
-                        .loginPage("/html/login.html").permitAll() // Using a customized login page
-                        .defaultSuccessUrl("/html/matchSchedule.html", true) // After successful login, you will be redirected to this page
-                        .failureUrl("/html/login.html?error=true") // You will be redirected to this page after a failed login
+                        .loginPage("/html/login.html").permitAll() // 使用自定义页面
+                        .defaultSuccessUrl("/html/matchSchedule.html", true) // 登录成功后跳转
+                        .failureUrl("/html/login.html?error=true") // 登录失败后跳转
                 )
-
-                // Basic authentication configuration (for API access)
-                .httpBasic(withDefaults()) // Basic Auth is supported for API calls
-
-                // Logout Configuration
-                .logout(logout -> logout
-                        .logoutUrl("/logout") // Set the logout path
-                        .logoutSuccessUrl("/html/login.html")  // The path to which you are redirected after the logout is successful
-                        .invalidateHttpSession(true)          // Invalidate the session
-                        .clearAuthentication(true)            // Clear authentication information
-                );
+                .logout().logoutUrl("/logout").logoutSuccessUrl("/"); // 登出配置
         return http.build();
     }
 
 
-
     @Bean
     public UserDetailsService userDetailsService() {
         UserDetails user =
@@ -90,7 +70,7 @@ public class SecurityConfig {
 
     @Bean
     public BCryptPasswordEncoder passwordEncoder() {
-        return new BCryptPasswordEncoder();     // Use BCrypt for password encryption
+        return new BCryptPasswordEncoder();
     }
 }
 
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index a025cc6041e1be9ce644d6cdb23ec401ff2e70e6..59586e3f5bac34c5411ad3b7cfa4e13e2aebf1e4 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -9,4 +9,5 @@ spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
 #spring.mvc.view.suffix=.html                  # ????
 spring.thymeleaf.prefix=classpath:/templates/
 spring.thymeleaf.suffix=.html
-spring.thymeleaf.mode=HTML
\ No newline at end of file
+spring.thymeleaf.mode=HTML
+image.upload.dir=./uploads
\ No newline at end of file
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
index 82dc0657f8670edb6b2ff9196240444d93ac8912..121020aa60db2d055e5cb47010ab7dc5b6cc35bb 100644
--- a/src/main/resources/schema.sql
+++ b/src/main/resources/schema.sql
@@ -46,7 +46,6 @@ CREATE TABLE IF NOT EXISTS ranking (
 );
 CREATE TABLE images (
                         id INT AUTO_INCREMENT PRIMARY KEY,
-                        filename VARCHAR(255) NOT NULL,  -- Image file name or path
-                        uploader VARCHAR(100) NOT NULL,  -- Uploader Name
-                        upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Upload time
+                        url VARCHAR(255) NOT NULL,  -- 图片文件名或路径
+                        uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP-- 上传时间
 );
diff --git a/src/main/resources/static/css/gallery.css b/src/main/resources/static/css/gallery.css
new file mode 100644
index 0000000000000000000000000000000000000000..0404d3e5d594bcdb5f44fe1ba4397b4f29cf5a9e
--- /dev/null
+++ b/src/main/resources/static/css/gallery.css
@@ -0,0 +1,89 @@
+/* General Styles for Gallery */
+.gallery-container {
+    max-width: 1200px;
+    margin: 20px auto;
+    padding: 20px;
+    text-align: center;
+    font-family: Arial, sans-serif;
+    background-color: #f9f9f9;
+    border-radius: 8px;
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.gallery-container h1 {
+    margin-bottom: 20px;
+    font-size: 2em;
+    color: #333;
+}
+
+/* Carousel Styles */
+.carousel {
+    position: relative;
+    overflow: hidden;
+    width: 80%;
+    margin: 0 auto;
+    max-height: 400px;
+}
+
+#carousel-images {
+    display: flex;
+    transition: transform 0.5s ease-in-out;
+}
+
+#carousel-images img {
+    max-width: 100%;
+    max-height: 400px;
+    object-fit: cover;
+    border-radius: 8px;
+}
+
+.carousel-button {
+    position: absolute;
+    top: 50%;
+    transform: translateY(-50%);
+    background-color: rgba(0, 0, 0, 0.5);
+    color: white;
+    border: none;
+    padding: 10px;
+    cursor: pointer;
+    border-radius: 50%;
+    font-size: 1.5em;
+}
+
+.carousel-button.prev {
+    left: 10px;
+}
+
+.carousel-button.next {
+    right: 10px;
+}
+
+.carousel-button:hover {
+    background-color: rgba(0, 0, 0, 0.8);
+}
+
+/* Upload Section */
+.upload-section {
+    margin-top: 20px;
+}
+
+.upload-btn {
+    background-color: #007bff;
+    color: white;
+    border: none;
+    padding: 10px 20px;
+    font-size: 1em;
+    border-radius: 4px;
+    cursor: pointer;
+    transition: background-color 0.3s ease;
+}
+
+.upload-btn:hover {
+    background-color: #0056b3;
+}
+.carousel img {
+    display: block;          /* Ensure the image displays as a block-level element */
+    margin: 0 auto;          /* Horizontally centers the image */
+    max-width: 100%;         /* Keep image responsive */
+    height: auto;
+}
\ No newline at end of file
diff --git a/src/main/resources/static/html/gallery.html b/src/main/resources/static/html/gallery.html
new file mode 100644
index 0000000000000000000000000000000000000000..71a86f2db25eec323ba3cd86bcc15170d7d08dbd
--- /dev/null
+++ b/src/main/resources/static/html/gallery.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>image gallery</title>
+    <link rel="stylesheet" href="/css/navBar.css">
+    <link rel="stylesheet" href="/css/footer.css">
+    <link rel="stylesheet" href="/css/gallery.css">
+</head>
+<body>
+<div id="navbar-container"></div>
+
+<!-- Main Content -->
+<main class="gallery-container">
+    <h1>Image Gallery</h1>
+
+    <!-- Carousel -->
+    <div class="carousel">
+        <div id="carousel-images">
+            <!-- Images will be dynamically inserted here -->
+        </div>
+        <button class="carousel-button prev" onclick="prevImage()">&#10094;</button>
+        <button class="carousel-button next" onclick="nextImage()">&#10095;</button>
+    </div>
+
+    <!-- Upload Section -->
+    <div class="upload-section">
+        <button class="upload-btn" onclick="document.getElementById('file-input').click()">Upload Image</button>
+        <input type="file" id="file-input" accept="image/*" style="display: none;" onchange="uploadImage(event)">
+    </div>
+</main>
+
+<!-- Footer Section -->
+<div id="footer-container"></div>
+
+<!-- JavaScript -->
+<script src="/js/navBar.js"></script>
+<script src="/js/footer.js"></script>
+<script src="/js/gallery.js"></script>
+<script>
+    // Load header and footer
+    loadNavbar('navbar-container');
+    loadFooter('footer-container');
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/resources/static/html/navBar.html b/src/main/resources/static/html/navBar.html
index 4d9895c801b6ca59aa56f193fb168e9806501275..c1e68cafbb857d9ddc335d6ed4f92106a7a89f1c 100644
--- a/src/main/resources/static/html/navBar.html
+++ b/src/main/resources/static/html/navBar.html
@@ -5,7 +5,7 @@
 <!--    put nav links here -->
     <ul class="nav-links">
         <li><a href="#">News</a></li>
-        <li><a href="#">Videos</a></li>
+        <li><a href="http://localhost:8080/html/gallery.html">Gallery</a></li>
         <li><a href="http://localhost:8080/html/rules.html">Rules</a></li>
         <li><a href="http://localhost:8080/html/UserCenter.html">Players</a></li>
         <li><a href="http://localhost:8080/html/matchSchedule.html">Matches</a></li>
diff --git a/src/main/resources/static/html/ranking.html b/src/main/resources/static/html/ranking.html
index 2be8d2275c1f3958708e60d74d19678f7d0b0ffb..691ed42b53246422fa502a51bbbd800363ca12f8 100644
--- a/src/main/resources/static/html/ranking.html
+++ b/src/main/resources/static/html/ranking.html
@@ -52,6 +52,5 @@
   loadFooter('footer-container');
 </script>
 <script src="/js/rankingTable.js"></script>
-<!--<script src="/js/matchSchedule.js"></script>-->
 </body>
 </html>
\ No newline at end of file
diff --git a/src/main/resources/static/js/gallery.js b/src/main/resources/static/js/gallery.js
new file mode 100644
index 0000000000000000000000000000000000000000..68949a4ed82cabdaf0cfb421cc27e5db464190d6
--- /dev/null
+++ b/src/main/resources/static/js/gallery.js
@@ -0,0 +1,70 @@
+let currentIndex = 0;
+let images = []; // This will hold the URLs of the images
+
+// Load images into the carousel
+function loadImages() {
+    const carouselImages = document.getElementById("carousel-images");
+    carouselImages.innerHTML = ""; // Clear current images
+
+    images.forEach((imageUrl, index) => {
+        const img = document.createElement("img");
+        img.src = imageUrl; // Correctly use the URL for the image src
+        console.log(img.src)
+        img.alt = `Image ${index + 1}`;
+        img.style.display = index === currentIndex ? "block" : "none";
+        carouselImages.appendChild(img);
+    });
+}
+
+// Show previous image
+function prevImage() {
+    currentIndex = (currentIndex - 1 + images.length) % images.length;
+    loadImages();
+}
+
+// Show next image
+function nextImage() {
+    currentIndex = (currentIndex + 1) % images.length;
+    loadImages();
+}
+
+// Upload image
+function uploadImage(event) {
+    const file = event.target.files[0];
+    if (!file) return;
+
+    const formData = new FormData();
+    formData.append("image", file);
+
+    fetch("/api/images", {
+        method: "POST",
+        body: formData,
+    })
+        .then(response => {
+            if (!response.ok) {
+                throw new Error("Failed to upload image");
+            }
+            return response.json();
+        })
+        .then(data => {
+            images.push(data.url); // Assuming the server returns the image URL
+            loadImages();
+        })
+        .catch(error => {
+            console.error("Error uploading image:", error);
+        });
+}
+
+// Initialize the gallery on page load
+window.onload = () => {
+    fetch("/api/images")
+        .then(response => response.json())
+        .then(data => {
+            images = data.map(image => image.url); // Extract URLs from the server response
+            loadImages();
+        })
+        .catch(error => {
+            console.error("Error loading images:", error);
+        });
+};
+
diff --git a/uploads/2acbe313-8dea-40df-b480-b209a15e4263_dart-1418245.webp b/uploads/2acbe313-8dea-40df-b480-b209a15e4263_dart-1418245.webp
new file mode 100644
index 0000000000000000000000000000000000000000..7da8666fdc10a75cc88eb5541adedaf3967893d0
Binary files /dev/null and b/uploads/2acbe313-8dea-40df-b480-b209a15e4263_dart-1418245.webp differ
diff --git a/uploads/9c7253d7-9608-432e-ac38-f23a3521b080_pair-ping-pong-paddles-ball-260nw-2472167701.webp b/uploads/9c7253d7-9608-432e-ac38-f23a3521b080_pair-ping-pong-paddles-ball-260nw-2472167701.webp
new file mode 100644
index 0000000000000000000000000000000000000000..090a736346d3d720a6315a369dca22aaafba0ca3
Binary files /dev/null and b/uploads/9c7253d7-9608-432e-ac38-f23a3521b080_pair-ping-pong-paddles-ball-260nw-2472167701.webp differ