added hashing to registering new user

This commit is contained in:
Lorenz Hohermuth 2025-05-15 20:41:53 +02:00
parent 3408f01598
commit 36363716c1
6 changed files with 199 additions and 134 deletions

View File

@ -66,6 +66,28 @@
<artifactId>jasypt-spring-boot-starter</artifactId> <artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version> <version>3.0.5</version>
</dependency> </dependency>
<!-- Bouncy Castle for secure hashing and random salt generation -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.76</version>
</dependency>
<!-- Spring Security for password encoding (bcrypt) -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>6.1.2</version>
</dependency>
<!-- Apache Commons Codec for encoding (like Base64) -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -32,150 +32,153 @@ import java.util.stream.Collectors;
@RequestMapping("api/users") @RequestMapping("api/users")
public class UserController { public class UserController {
private UserService userService; private UserService userService;
private PasswordEncryptionService passwordService; private PasswordEncryptionService passwordService;
private final ConfigProperties configProperties; private final ConfigProperties configProperties;
private static final Logger logger = LoggerFactory.getLogger(UserController.class); private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired @Autowired
public UserController(ConfigProperties configProperties, UserService userService, public UserController(ConfigProperties configProperties, UserService userService,
PasswordEncryptionService passwordService) { PasswordEncryptionService passwordService) {
this.configProperties = configProperties; this.configProperties = configProperties;
System.out.println("UserController.UserController: cross origin: " + configProperties.getOrigin()); System.out.println("UserController.UserController: cross origin: " + configProperties.getOrigin());
// Logging in the constructor // Logging in the constructor
logger.info("UserController initialized: " + configProperties.getOrigin()); logger.info("UserController initialized: " + configProperties.getOrigin());
logger.debug("UserController.UserController: Cross Origin Config: {}", configProperties.getOrigin()); logger.debug("UserController.UserController: Cross Origin Config: {}", configProperties.getOrigin());
this.userService = userService; this.userService = userService;
this.passwordService = passwordService; this.passwordService = passwordService;
} }
// build create User REST API // build create User REST API
@CrossOrigin(origins = "${CROSS_ORIGIN}") @CrossOrigin(origins = "${CROSS_ORIGIN}")
@PostMapping @PostMapping
public ResponseEntity<String> createUser(@Valid @RequestBody RegisterUser registerUser, BindingResult bindingResult) { public ResponseEntity<String> createUser(@Valid @RequestBody RegisterUser registerUser, BindingResult bindingResult) {
//captcha //captcha
//todo ergänzen //todo ergänzen
System.out.println("UserController.createUser: captcha passed."); System.out.println("UserController.createUser: captcha passed.");
//input validation //input validation
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getFieldErrors().stream() List<String> errors = bindingResult.getFieldErrors().stream()
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()) .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
.collect(Collectors.toList()); .collect(Collectors.toList());
System.out.println("UserController.createUser " + errors); System.out.println("UserController.createUser " + errors);
JsonArray arr = new JsonArray(); JsonArray arr = new JsonArray();
errors.forEach(arr::add); errors.forEach(arr::add);
JsonObject obj = new JsonObject(); JsonObject obj = new JsonObject();
obj.add("message", arr); obj.add("message", arr);
String json = new Gson().toJson(obj); String json = new Gson().toJson(obj);
System.out.println("UserController.createUser, validation fails: " + json); System.out.println("UserController.createUser, validation fails: " + json);
return ResponseEntity.badRequest().body(json); return ResponseEntity.badRequest().body(json);
} }
System.out.println("UserController.createUser: input validation passed"); System.out.println("UserController.createUser: input validation passed");
//password validation //password validation
//todo ergänzen //todo ergänzen
System.out.println("UserController.createUser, password validation passed"); System.out.println("UserController.createUser, password validation passed");
//transform registerUser to user //transform registerUser to user
User user = new User( String salt = PasswordEncryptionService.generateSalt();
null,
registerUser.getFirstName(),
registerUser.getLastName(),
registerUser.getEmail(),
passwordService.hashPassword(registerUser.getPassword())
);
User savedUser = userService.createUser(user); User user = new User(
System.out.println("UserController.createUser, user saved in db"); null,
JsonObject obj = new JsonObject(); registerUser.getFirstName(),
obj.addProperty("answer", "User Saved"); registerUser.getLastName(),
String json = new Gson().toJson(obj); registerUser.getEmail(),
System.out.println("UserController.createUser " + json); passwordService.hashPassword(registerUser.getPassword(), salt),
return ResponseEntity.accepted().body(json); salt
} );
// build get user by id REST API User savedUser = userService.createUser(user);
// http://localhost:8080/api/users/1 System.out.println("UserController.createUser, user saved in db");
@CrossOrigin(origins = "${CROSS_ORIGIN}") JsonObject obj = new JsonObject();
@GetMapping("{id}") obj.addProperty("answer", "User Saved");
public ResponseEntity<User> getUserById(@PathVariable("id") Long userId) { String json = new Gson().toJson(obj);
User user = userService.getUserById(userId); System.out.println("UserController.createUser " + json);
return new ResponseEntity<>(user, HttpStatus.OK); return ResponseEntity.accepted().body(json);
} }
// Build Get All Users REST API // build get user by id REST API
// http://localhost:8080/api/users // http://localhost:8080/api/users/1
@CrossOrigin(origins = "${CROSS_ORIGIN}") @CrossOrigin(origins = "${CROSS_ORIGIN}")
@GetMapping @GetMapping("{id}")
public ResponseEntity<List<User>> getAllUsers() { public ResponseEntity<User> getUserById(@PathVariable("id") Long userId) {
List<User> users = userService.getAllUsers(); User user = userService.getUserById(userId);
return new ResponseEntity<>(users, HttpStatus.OK); return new ResponseEntity<>(user, HttpStatus.OK);
} }
// Build Update User REST API // Build Get All Users REST API
// http://localhost:8080/api/users/1 // http://localhost:8080/api/users
@CrossOrigin(origins = "${CROSS_ORIGIN}") @CrossOrigin(origins = "${CROSS_ORIGIN}")
@PutMapping("{id}") @GetMapping
public ResponseEntity<User> updateUser(@PathVariable("id") Long userId, public ResponseEntity<List<User>> getAllUsers() {
@RequestBody User user) { List<User> users = userService.getAllUsers();
user.setId(userId); return new ResponseEntity<>(users, HttpStatus.OK);
User updatedUser = userService.updateUser(user); }
return new ResponseEntity<>(updatedUser, HttpStatus.OK);
}
// Build Delete User REST API // Build Update User REST API
@CrossOrigin(origins = "${CROSS_ORIGIN}") // http://localhost:8080/api/users/1
@DeleteMapping("{id}") @CrossOrigin(origins = "${CROSS_ORIGIN}")
public ResponseEntity<String> deleteUser(@PathVariable("id") Long userId) { @PutMapping("{id}")
userService.deleteUser(userId); public ResponseEntity<User> updateUser(@PathVariable("id") Long userId,
return new ResponseEntity<>("User successfully deleted!", HttpStatus.OK); @RequestBody User user) {
} user.setId(userId);
User updatedUser = userService.updateUser(user);
return new ResponseEntity<>(updatedUser, HttpStatus.OK);
}
// Build Delete User REST API
@CrossOrigin(origins = "${CROSS_ORIGIN}")
@DeleteMapping("{id}")
public ResponseEntity<String> deleteUser(@PathVariable("id") Long userId) {
userService.deleteUser(userId);
return new ResponseEntity<>("User successfully deleted!", HttpStatus.OK);
}
// get user id by email // get user id by email
@CrossOrigin(origins = "${CROSS_ORIGIN}") @CrossOrigin(origins = "${CROSS_ORIGIN}")
@PostMapping("/byemail") @PostMapping("/byemail")
public ResponseEntity<String> getUserIdByEmail(@RequestBody EmailAdress email, BindingResult bindingResult) { public ResponseEntity<String> getUserIdByEmail(@RequestBody EmailAdress email, BindingResult bindingResult) {
System.out.println("UserController.getUserIdByEmail: " + email); System.out.println("UserController.getUserIdByEmail: " + email);
//input validation //input validation
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getFieldErrors().stream() List<String> errors = bindingResult.getFieldErrors().stream()
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()) .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
.collect(Collectors.toList()); .collect(Collectors.toList());
System.out.println("UserController.createUser " + errors); System.out.println("UserController.createUser " + errors);
JsonArray arr = new JsonArray(); JsonArray arr = new JsonArray();
errors.forEach(arr::add); errors.forEach(arr::add);
JsonObject obj = new JsonObject(); JsonObject obj = new JsonObject();
obj.add("message", arr); obj.add("message", arr);
String json = new Gson().toJson(obj); String json = new Gson().toJson(obj);
System.out.println("UserController.createUser, validation fails: " + json); System.out.println("UserController.createUser, validation fails: " + json);
return ResponseEntity.badRequest().body(json); return ResponseEntity.badRequest().body(json);
} }
System.out.println("UserController.getUserIdByEmail: input validation passed"); System.out.println("UserController.getUserIdByEmail: input validation passed");
User user = userService.findByEmail(email.getEmail()); User user = userService.findByEmail(email.getEmail());
if (user == null) { if (user == null) {
System.out.println("UserController.getUserIdByEmail, no user found with email: " + email); System.out.println("UserController.getUserIdByEmail, no user found with email: " + email);
JsonObject obj = new JsonObject(); JsonObject obj = new JsonObject();
obj.addProperty("message", "No user found with this email"); obj.addProperty("message", "No user found with this email");
String json = new Gson().toJson(obj); String json = new Gson().toJson(obj);
System.out.println("UserController.getUserIdByEmail, fails: " + json); System.out.println("UserController.getUserIdByEmail, fails: " + json);
return ResponseEntity.badRequest().body(json); return ResponseEntity.badRequest().body(json);
} }
System.out.println("UserController.getUserIdByEmail, user find by email"); System.out.println("UserController.getUserIdByEmail, user find by email");
JsonObject obj = new JsonObject(); JsonObject obj = new JsonObject();
obj.addProperty("answer", user.getId()); obj.addProperty("answer", user.getId());
String json = new Gson().toJson(obj); String json = new Gson().toJson(obj);
System.out.println("UserController.getUserIdByEmail " + json); System.out.println("UserController.getUserIdByEmail " + json);
return ResponseEntity.accepted().body(json); return ResponseEntity.accepted().body(json);
} }
} }

View File

@ -32,4 +32,7 @@ public class User {
@Column(nullable = false) @Column(nullable = false)
private String password; private String password;
@Column(nullable = false)
private String salt;
} }

View File

@ -1,21 +1,37 @@
package ch.bbw.pr.tresorbackend.service; package ch.bbw.pr.tresorbackend.service;
import lombok.Value;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.security.SecureRandom;
/** /**
* PasswordEncryptionService * PasswordEncryptionService
*
* @author Peter Rutschmann * @author Peter Rutschmann
*/ */
@Service @Service
public class PasswordEncryptionService { public class PasswordEncryptionService {
//todo ergänzen!
public PasswordEncryptionService() { public PasswordEncryptionService() {
//todo anpassen! //todo anpassen!
} }
public String hashPassword(String password) { public static String generateSalt() {
//todo anpassen! byte[] salt = new byte[5];
return password; new SecureRandom().nextBytes(salt);
} return Hex.toHexString(salt);
}
public String hashPassword(String password, String salt) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String pepper = new PepperService().getPepper();
return encoder.encode(pepper + password );
}
} }
record PasswordBean(String hashedPassword, String Salt) {
}

View File

@ -0,0 +1,19 @@
package ch.bbw.pr.tresorbackend.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class PepperService {
@Value("${pepper}")
private String pepper;
public String getPepper() {
return pepper;
}
public void printPepper() {
System.out.println("Pepper value: " + pepper);
}
}

View File

@ -8,4 +8,6 @@ spring.datasource.password=1234
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update
CROSS_ORIGIN=http://localhost:3000 CROSS_ORIGIN=http://localhost:3000
pepper=VfQqM