mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-03-16 00:50:47 +08:00
feat: add email verification
This commit is contained in:
@@ -8,6 +8,7 @@ import lombok.Data;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/auth")
|
@RequestMapping("/api/auth")
|
||||||
@@ -25,9 +26,17 @@ public class AuthController {
|
|||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public ResponseEntity<?> register(@RequestBody RegisterRequest req) {
|
public ResponseEntity<?> register(@RequestBody RegisterRequest req) {
|
||||||
User user = userService.register(req.getUsername(), req.getEmail(), req.getPassword());
|
User user = userService.register(req.getUsername(), req.getEmail(), req.getPassword());
|
||||||
emailService.sendEmail(user.getEmail(), "Welcome to OpenIsle", "Thank you for registering.");
|
emailService.sendEmail(user.getEmail(), "Verification Code", "Your verification code is " + user.getVerificationCode());
|
||||||
String token = jwtService.generateToken(user.getUsername());
|
return ResponseEntity.ok(Map.of("message", "Verification code sent"));
|
||||||
return ResponseEntity.ok(new JwtResponse(token));
|
}
|
||||||
|
|
||||||
|
@PostMapping("/verify")
|
||||||
|
public ResponseEntity<?> verify(@RequestBody VerifyRequest req) {
|
||||||
|
boolean ok = userService.verifyCode(req.getUsername(), req.getCode());
|
||||||
|
if (ok) {
|
||||||
|
return ResponseEntity.ok(Map.of("message", "Verified"));
|
||||||
|
}
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("error", "Invalid verification code"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +48,7 @@ public class AuthController {
|
|||||||
public ResponseEntity<?> login(@RequestBody LoginRequest req) {
|
public ResponseEntity<?> login(@RequestBody LoginRequest req) {
|
||||||
return userService.authenticate(req.getUsername(), req.getPassword())
|
return userService.authenticate(req.getUsername(), req.getPassword())
|
||||||
.map(user -> ResponseEntity.ok(new JwtResponse(jwtService.generateToken(user.getUsername()))))
|
.map(user -> ResponseEntity.ok(new JwtResponse(jwtService.generateToken(user.getUsername()))))
|
||||||
.orElse(ResponseEntity.status(401).build());
|
.orElse(ResponseEntity.status(401).body(Map.of("error", "Invalid credentials or user not verified")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@@ -55,6 +64,12 @@ public class AuthController {
|
|||||||
private String password;
|
private String password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
private static class VerifyRequest {
|
||||||
|
private String username;
|
||||||
|
private String code;
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
private static class JwtResponse {
|
private static class JwtResponse {
|
||||||
private final String token;
|
private final String token;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.openisle.controller;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseEntity<?> handleException(Exception ex) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("error", ex.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@ package com.openisle.controller;
|
|||||||
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class HelloController {
|
public class HelloController {
|
||||||
@@ -10,7 +11,7 @@ public class HelloController {
|
|||||||
* -H "Authorization: Bearer <jwt-token>"
|
* -H "Authorization: Bearer <jwt-token>"
|
||||||
*/
|
*/
|
||||||
@GetMapping("/api/hello")
|
@GetMapping("/api/hello")
|
||||||
public String hello() {
|
public Map<String, String> hello() {
|
||||||
return "Hello, Authenticated User";
|
return Map.of("message", "Hello, Authenticated User");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,9 @@ public class User {
|
|||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private boolean verified = false;
|
||||||
|
|
||||||
|
private String verificationCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -17,17 +18,33 @@ public class UserService {
|
|||||||
|
|
||||||
public User register(String username, String email, String password) {
|
public User register(String username, String email, String password) {
|
||||||
if (userRepository.findByUsername(username).isPresent() || userRepository.findByEmail(email).isPresent()) {
|
if (userRepository.findByUsername(username).isPresent() || userRepository.findByEmail(email).isPresent()) {
|
||||||
throw new RuntimeException("User already exists");
|
throw new IllegalStateException("User already exists");
|
||||||
}
|
}
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setUsername(username);
|
user.setUsername(username);
|
||||||
user.setEmail(email);
|
user.setEmail(email);
|
||||||
user.setPassword(passwordEncoder.encode(password));
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
|
user.setVerified(false);
|
||||||
|
String code = String.format("%06d", new Random().nextInt(1000000));
|
||||||
|
user.setVerificationCode(code);
|
||||||
return userRepository.save(user);
|
return userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean verifyCode(String username, String code) {
|
||||||
|
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||||
|
if (userOpt.isPresent() && code.equals(userOpt.get().getVerificationCode())) {
|
||||||
|
User user = userOpt.get();
|
||||||
|
user.setVerified(true);
|
||||||
|
user.setVerificationCode(null);
|
||||||
|
userRepository.save(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public Optional<User> authenticate(String username, String password) {
|
public Optional<User> authenticate(String username, String password) {
|
||||||
return userRepository.findByUsername(username)
|
return userRepository.findByUsername(username)
|
||||||
|
.filter(User::isVerified)
|
||||||
.filter(user -> passwordEncoder.matches(password, user.getPassword()));
|
.filter(user -> passwordEncoder.matches(password, user.getPassword()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user