Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.bookingtour.sun.config;

import com.bookingtour.sun.entity.User;
import com.bookingtour.sun.enums.LoginProvider;
import com.bookingtour.sun.enums.UserRole;
import com.bookingtour.sun.enums.UserStatus;
import com.bookingtour.sun.repository.UserRepository;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class OAuth2LoginSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler {

@Autowired
UserRepository userRepository;

@Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {

OAuth2User user = (OAuth2User) authentication.getPrincipal();

String email = user.getAttribute("email");

String name = user.getAttribute("name");


User dbUser = userRepository.findByEmail(email)
.orElseGet(() -> {
User u = new User();
u.setEmail(email);
u.setFullName(name);
String username = name != null ? name : email;
if (username != null && username.length() > 50) {
username = username.substring(0, 50);
}
u.setUsername(username);
u.setRole(UserRole.USER);
u.setStatus(UserStatus.ACTIVE);
u.setPasswordDigest("");
u.setLoginProvider(LoginProvider.GOOGLE);
return userRepository.save(u);
});

super.onAuthenticationSuccess(
request,
response,
authentication);
}
}
30 changes: 25 additions & 5 deletions src/main/java/com/bookingtour/sun/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
package com.bookingtour.sun.config;

import com.bookingtour.sun.enums.UserRole;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

private final OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

http
.authorizeHttpRequests(auth -> auth

// public pages
.requestMatchers(
"/tours",
"/login",
"/register",

"/css/**",
"/js/**",
"/images/**"
"/images/**",
"/uploads/**",

"/login/oauth2/**",
"/oauth2/**"
)
.permitAll()
.requestMatchers(HttpMethod.GET,
"/",
"/tours/**"
).permitAll()
// admin page
.requestMatchers("/admin/**")
.hasAnyRole(UserRole.ADMIN.name())
// client page
.requestMatchers("/")
.hasAnyRole(UserRole.ADMIN.name(), UserRole.USER.name())
// booking phải login
.requestMatchers("/bookings/**")
.authenticated()

.anyRequest()
.authenticated()
Expand All @@ -46,6 +60,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll()
)

.oauth2Login(oauth -> oauth
.loginPage("/login")
.defaultSuccessUrl("/")
.successHandler(oAuth2LoginSuccessHandler)
);

return http.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.bookingtour.sun.dto.request.PageResponse;
import com.bookingtour.sun.dto.response.BookingReponse;
import com.bookingtour.sun.services.BookingService;
import com.bookingtour.sun.services.CurrentUserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -13,9 +14,7 @@
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
Expand All @@ -24,12 +23,13 @@
@Slf4j
public class BookingController {
private final BookingService bookingService;
private final CurrentUserService currentUserService;

@GetMapping("")
public String listBooking(@AuthenticationPrincipal UserDetails userDetails,
Model model,
public String listBooking(Model model,
BookingSearchRequest request) {
String email = userDetails.getUsername();
String email = currentUserService.getCurrentUserEmail();

PageResponse<BookingReponse> bookings = bookingService.getMyBooking(email, request);
model.addAttribute("bookings", bookings.getContent());

Expand All @@ -51,8 +51,8 @@ public String createBooking(@AuthenticationPrincipal UserDetails userDetails,
redirectAttributes.addFlashAttribute("errorMessage", "Thông tin đặt tour không hợp lệ");
return "redirect:/tours/" + request.getTourId();
}
String email = userDetails.getUsername();
try {
String email = currentUserService.getCurrentUserEmail();
bookingService.createBooking(email, request);
redirectAttributes.addFlashAttribute("successMessage", "Đặt tour thành công");
return "redirect:/bookings";
Expand All @@ -62,4 +62,21 @@ public String createBooking(@AuthenticationPrincipal UserDetails userDetails,
return "redirect:/tours/" + request.getTourId();
}
}

@PostMapping("/{id}/cancel")
public String cancelBooking(
@PathVariable Long id,
RedirectAttributes redirectAttributes
) {
try {
String email = currentUserService.getCurrentUserEmail();
bookingService.cancelBooking(id, email);
redirectAttributes.addFlashAttribute("successMessage", "Hủy đặt tour thành công");

} catch (Exception e) {
redirectAttributes.addFlashAttribute("errorMessage", e.getMessage());
}

return "redirect:/bookings";
}
}
34 changes: 34 additions & 0 deletions src/main/java/com/bookingtour/sun/controller/ClientController.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package com.bookingtour.sun.controller;
import com.bookingtour.sun.dto.request.RegisterRequest;
import com.bookingtour.sun.dto.response.TourResponse;
import com.bookingtour.sun.services.TourService;
import com.bookingtour.sun.services.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.List;

@Controller
@RequiredArgsConstructor
public class ClientController {
private final TourService tourService;
private final UserService userService;

@GetMapping("/")
public String home(Model model) {
Expand All @@ -26,4 +33,31 @@ public String login(Model model) {
model.addAttribute("title", "Login - Tour Booking");
return "client/login";
}

@GetMapping("/register")
public String register(Model model) {
model.addAttribute("title", "Register - Tour Booking");
model.addAttribute("user", new RegisterRequest());
return "client/register";
}

@PostMapping("/register")
public String register(
@Valid @ModelAttribute("user") RegisterRequest request,
BindingResult result,
Model model) {

if (!request.getPassword().equals(request.getConfirmPassword())) {
model.addAttribute("passwordError", "Passwords do not match.");
return "client/register";
}

if (result.hasErrors()) {
return "client/register";
}

userService.register(request);

return "redirect:/login?registered";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.bookingtour.sun.controller;

import com.bookingtour.sun.entity.Payment;
import com.bookingtour.sun.exception.ResourceNotFoundException;
import com.bookingtour.sun.exception.UnauthorizedException;
import com.bookingtour.sun.repository.PaymentRepository;
import com.bookingtour.sun.services.CurrentUserService;
import com.bookingtour.sun.services.PaymentService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
@RequestMapping("/payments")
@RequiredArgsConstructor
public class PaymentController {
private final PaymentService paymentService;
private final CurrentUserService currentUserService;
private final PaymentRepository paymentRepository;


@PostMapping("/{bookingId}/create")
public String createPayment(@PathVariable Long bookingId, RedirectAttributes redirectAttributes)
{
try {
String email = currentUserService.getCurrentUserEmail();
String url = paymentService.createPayment(bookingId, email);
return "redirect:" + url;
} catch (Exception e) {
redirectAttributes.addFlashAttribute("errorMessage", "Thanh toán thất bại");
return "redirect:/bookings";
}
}

@GetMapping("/mock/{id}")
public String mockPayment(
@PathVariable Long id,
Model model,
RedirectAttributes redirectAttributes
){
try {
String email = currentUserService.getCurrentUserEmail();
Payment payment = paymentRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Payment not found with id: " + id));
if(!payment.getBooking().getUser().getEmail().equals(email)) {
throw new UnauthorizedException("Invalid booking");
}
model.addAttribute("paymentId", id);
return "client/payment/mock";
} catch (Exception e) {
redirectAttributes.addFlashAttribute("errorMessage", "Thanh toán thất bại");
return "redirect:/bookings";
}
}

@PostMapping("/{id}/success")
public String success(@PathVariable Long id, RedirectAttributes redirectAttributes)
{
try {
String email = currentUserService.getCurrentUserEmail();
paymentService.success(id, email);
redirectAttributes.addFlashAttribute("successMessage", "Thanh toán thành công");
} catch (Exception e) {
redirectAttributes.addFlashAttribute("errorMessage", "Thanh toán thất bại");
}

return "redirect:/bookings";
}
}
Loading