Selaa lähdekoodia

Add user data to templates

Ryan Wright 1 kuukausi sitten
vanhempi
säilyke
b00b624f2b

+ 17 - 0
src/main/java/org/example/sweater/config/MvcConfig.java

@@ -1,7 +1,10 @@
 package org.example.sweater.config;
 
+import org.example.sweater.controller.CSRFInterceptor;
+import org.example.sweater.controller.SessionInterceptor;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@@ -10,6 +13,11 @@ public class MvcConfig implements WebMvcConfigurer {
     @Value("${upload.path}")
     private String uploadPath;
 
+    public MvcConfig(CSRFInterceptor csrfInterceptor, SessionInterceptor sessionInterceptor) {
+        this.csrfInterceptor = csrfInterceptor;
+        this.sessionInterceptor = sessionInterceptor;
+    }
+
     @Override
     public void addResourceHandlers(ResourceHandlerRegistry registry) {
         registry.addResourceHandler("/static/**")
@@ -17,4 +25,13 @@ public class MvcConfig implements WebMvcConfigurer {
         registry.addResourceHandler("/img/**")
                 .addResourceLocations("file:" + uploadPath + "/");
     }
+
+    private final CSRFInterceptor csrfInterceptor;
+    private final SessionInterceptor sessionInterceptor;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(csrfInterceptor);
+        registry.addInterceptor(sessionInterceptor);
+    }
 }

+ 0 - 6
src/main/java/org/example/sweater/controller/AuthorizationController.java

@@ -22,12 +22,6 @@ public class AuthorizationController {
 
     private final UsersRepository usersRepository;
 
-    @ModelAttribute
-    public void addCsrfToken(Model model, HttpServletRequest request) {
-        CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
-        model.addAttribute("_csrf", token);
-    }
-
     @GetMapping("/login")
     public String login() {
         return "login";

+ 22 - 0
src/main/java/org/example/sweater/controller/CSRFInterceptor.java

@@ -0,0 +1,22 @@
+package org.example.sweater.controller;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.web.csrf.CsrfToken;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+@Component
+public class CSRFInterceptor implements HandlerInterceptor {
+    @Override
+    public boolean preHandle(
+            HttpServletRequest request,
+            HttpServletResponse response,
+            Object handler
+    ) {
+        CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
+        request.setAttribute("_csrf", token);
+
+        return true;
+    }
+}

+ 2 - 10
src/main/java/org/example/sweater/controller/MainController.java

@@ -1,22 +1,20 @@
 package org.example.sweater.controller;
 
-import jakarta.servlet.http.HttpServletRequest;
 import org.example.sweater.domain.Message;
 import org.example.sweater.domain.User;
 import org.example.sweater.repos.MessagesRepository;
 import org.example.sweater.service.FileService;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
-import org.springframework.security.web.csrf.CsrfToken;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
 import java.util.Map;
+import java.util.Objects;
 
 @Controller
 public class MainController {
@@ -28,12 +26,6 @@ public class MainController {
     private final MessagesRepository messagesRepository;
     private final FileService fileService;
 
-    @ModelAttribute
-    public void addCsrfToken(Model model, HttpServletRequest request) {
-        CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
-        model.addAttribute("_csrf", token);
-    }
-
     @GetMapping("/")
     public String mainPage() {
         return "main";
@@ -74,7 +66,7 @@ public class MainController {
     ) throws IOException {
         Message createdMessage = new Message(text, tag, user);
 
-        if (file != null) {
+        if (file != null && !Objects.requireNonNull(file.getOriginalFilename()).isEmpty()) {
             String savedFileName = fileService.saveFile(file);
 
             createdMessage.setFilename(savedFileName);

+ 24 - 0
src/main/java/org/example/sweater/controller/SessionInterceptor.java

@@ -0,0 +1,24 @@
+package org.example.sweater.controller;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.example.sweater.domain.User;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import java.security.Principal;
+
+@Component
+public class SessionInterceptor implements HandlerInterceptor {
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        Principal principal = request.getUserPrincipal();
+        if (principal != null) {
+            User user = (User) ((Authentication) principal).getPrincipal();
+            request.setAttribute("user", user);
+        }
+
+        return true;
+    }
+}

+ 3 - 3
src/main/java/org/example/sweater/domain/Message.java

@@ -16,9 +16,9 @@ public class Message {
     private Long id;
 
     private String text;
-    public void setText(String text) {
-        this.text = text;
-    }
+//    public void setText(String text) {
+//        this.text = text;
+//    }
     public String getText() {
         return text;
     }

+ 4 - 0
src/main/java/org/example/sweater/domain/User.java

@@ -23,6 +23,10 @@ public class User implements UserDetails {
     @Enumerated(EnumType.STRING)
     private Set<Role> roles;
 
+    public boolean isAdmin() {
+        return roles.contains(Role.ADMIN);
+    }
+
     public Long getId() {
         return id;
     }

+ 1 - 3
src/main/java/org/example/sweater/service/FileService.java

@@ -22,9 +22,7 @@ public class FileService {
 
     private final String uploadAbsoluteDir;
 
-    public String saveFile(MultipartFile file) throws IOException, RuntimeException {
-        if (Objects.requireNonNull(file.getOriginalFilename()).isEmpty()) throw new RuntimeException("File name is empty");
-
+    public String saveFile(MultipartFile file) throws IOException {
         String uuidFile = UUID.randomUUID().toString();
         String fileName = uuidFile + "_" + file.getOriginalFilename();
 

+ 0 - 4
src/main/resources/static/styles.css

@@ -1,4 +0,0 @@
-body {
-    background: #ddd;
-    color: #111;
-}

+ 2 - 2
src/main/resources/templates/main.ftl

@@ -1,6 +1,6 @@
 <#import "parts/common.ftl" as common>
 
 <@common.page>
-    <div>Hello, user</div>
-    <a href="/feed">Feed</a>
+    <h2>Hello, guest</h2>
+    <p>This is a dumb X clone</p>
 </@common.page>

+ 6 - 1
src/main/resources/templates/parts/common.ftl

@@ -5,9 +5,14 @@
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <link rel="stylesheet" href="/static/styles.css">
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
 </head>
 <body>
-<#nested>
+<#include "navbar.ftl">
+<div class="container mt-5">
+    <#nested>
+</div>
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
 </body>
 </html>
 </#macro>

+ 25 - 0
src/main/resources/templates/parts/navbar.ftl

@@ -0,0 +1,25 @@
+<#include "security.ftl">
+
+<nav class="navbar navbar-expand-lg bg-body-tertiary">
+    <div class="container-fluid">
+        <a class="navbar-brand" href="/">Sweater</a>
+        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
+            <span class="navbar-toggler-icon"></span>
+        </button>
+        <div class="collapse navbar-collapse" id="navbarNav">
+            <ul class="navbar-nav">
+                <li class="nav-item">
+                    <a class="nav-link" aria-current="page" href="/">Home</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" aria-current="page" href="/feed">Messages</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" aria-current="page" href="/user">Users</a>
+                </li>
+            </ul>
+
+            <div class="navbar-text">${name}</div>
+        </div>
+    </div>
+</nav>

+ 14 - 0
src/main/resources/templates/parts/security.ftl

@@ -0,0 +1,14 @@
+<#assign
+    know = user??
+>
+<#if know>
+    <#assign
+        name = user.getUsername()
+        isAdmin = user.isAdmin()
+    >
+<#else>
+    <#assign
+        name = "unknown"
+        isAdmin = false
+    >
+</#if>