Skip to content

Commit 9f03d85

Browse files
authored
Merge pull request #7 from ithsjava25/feature/auth-login
Implement login with DTO and secure password validation
2 parents c961c67 + 6e95838 commit 9f03d85

7 files changed

Lines changed: 133 additions & 0 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.example.alfs.config;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
6+
import org.springframework.security.crypto.password.PasswordEncoder;
7+
8+
@Configuration
9+
public class PasswordConfig {
10+
11+
// Makes password encoder available in the whole app
12+
@Bean
13+
public PasswordEncoder passwordEncoder(){
14+
return new BCryptPasswordEncoder();
15+
}
16+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.example.alfs.controllers;
2+
3+
import org.example.alfs.dto.auth.LoginRequestDTO;
4+
import org.example.alfs.dto.auth.LoginResponseDTO;
5+
import org.example.alfs.entities.User;
6+
import org.example.alfs.services.AuthService;
7+
import org.springframework.web.bind.annotation.PostMapping;
8+
import org.springframework.web.bind.annotation.RequestBody;
9+
import org.springframework.web.bind.annotation.RequestMapping;
10+
import org.springframework.web.bind.annotation.RestController;
11+
import jakarta.validation.Valid;
12+
13+
@RestController
14+
@RequestMapping("/auth")
15+
public class AuthController {
16+
17+
private final AuthService authService;
18+
19+
public AuthController(AuthService authService) {
20+
this.authService = authService;
21+
}
22+
23+
/**
24+
* Handles user login by validating credentials and returning user details.
25+
*/
26+
@PostMapping("/login")
27+
public LoginResponseDTO login(@Valid @RequestBody LoginRequestDTO request) {
28+
29+
User user = authService.login(
30+
request.getUsername(),
31+
request.getPassword()
32+
);
33+
34+
return new LoginResponseDTO(
35+
user.getUsername(),
36+
user.getRole().name()
37+
);
38+
}
39+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.example.alfs.dto.auth;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
@Getter
8+
@Setter
9+
public class LoginRequestDTO {
10+
11+
@NotBlank(message = "Username is required")
12+
private String username;
13+
14+
@NotBlank(message = "Password is required")
15+
private String password;
16+
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.example.alfs.dto.auth;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
@Getter
7+
@AllArgsConstructor
8+
public class LoginResponseDTO {
9+
10+
private String username;
11+
private String role;
12+
}

src/main/java/org/example/alfs/entities/User.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ public class User {
2727
private String passwordHash;
2828

2929
@Enumerated(EnumType.STRING)
30+
@Column(nullable = false)
3031
private Role role;
3132

33+
@PrePersist
34+
public void prePersist() {
35+
if (role == null) {
36+
role = Role.REPORTER;
37+
}
38+
}
39+
3240
}

src/main/java/org/example/alfs/repositories/UserRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88
public interface UserRepository extends JpaRepository<User, Long> {
99

10+
Optional<User> findByUsername(String username);
1011
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.example.alfs.services;
2+
3+
import org.example.alfs.entities.User;
4+
import org.example.alfs.repositories.UserRepository;
5+
import org.springframework.http.HttpStatus;
6+
import org.springframework.security.crypto.password.PasswordEncoder;
7+
import org.springframework.stereotype.Service;
8+
import org.springframework.web.server.ResponseStatusException;
9+
10+
@Service
11+
public class AuthService {
12+
13+
private final PasswordEncoder passwordEncoder;
14+
private final UserRepository userRepository;
15+
16+
public AuthService(PasswordEncoder passwordEncoder, UserRepository userRepository) {
17+
this.passwordEncoder = passwordEncoder;
18+
this.userRepository = userRepository;
19+
}
20+
21+
/**
22+
* Authenticates a user by verifying username and password.
23+
*/
24+
public User login(String username, String password) {
25+
26+
User user = userRepository.findByUsername(username)
27+
.orElseThrow(() -> new ResponseStatusException(
28+
HttpStatus.UNAUTHORIZED,
29+
"Invalid username or password"
30+
));
31+
if (!passwordEncoder.matches(password, user.getPasswordHash())) {
32+
throw new ResponseStatusException(
33+
HttpStatus.UNAUTHORIZED,
34+
"Invalid username or password"
35+
);
36+
}
37+
38+
return user;
39+
}
40+
}

0 commit comments

Comments
 (0)