Compare commits

...

2 Commits

Author SHA1 Message Date
litoral05 5d422e1608 Add jwt token generation 2026-06-03 14:08:00 +01:00
litoral05 04f4732da1 Add login endpoint 2026-06-03 12:23:08 +01:00
11 changed files with 226 additions and 2 deletions
+19
View File
@@ -72,6 +72,25 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.7</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.7</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.7</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
@@ -1,12 +1,16 @@
package com.litoralregas.backend_gateway;
import com.litoralregas.backend_gateway.gateway.ProxyProperties;
import com.litoralregas.backend_gateway.security.JwtProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(ProxyProperties.class)
@EnableConfigurationProperties({
ProxyProperties.class,
JwtProperties.class
})
public class BackendGatewayApplication {
public static void main(String[] args) {
@@ -0,0 +1,21 @@
package com.litoralregas.backend_gateway.auth;
import com.litoralregas.backend_gateway.auth.dto.LoginRequest;
import com.litoralregas.backend_gateway.auth.dto.LoginResponse;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthService authService;
public AuthController(AuthService authService) {
this.authService = authService;
}
@PostMapping("/login")
public LoginResponse login(@RequestBody LoginRequest request) {
return authService.login(request);
}
}
@@ -0,0 +1,58 @@
package com.litoralregas.backend_gateway.auth;
import com.litoralregas.backend_gateway.auth.dto.LoginRequest;
import com.litoralregas.backend_gateway.auth.dto.LoginResponse;
import com.litoralregas.backend_gateway.security.JwtService;
import com.litoralregas.backend_gateway.user.UserEntity;
import com.litoralregas.backend_gateway.user.UserRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class AuthService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final JwtService jwtService;
public AuthService(
UserRepository userRepository,
PasswordEncoder passwordEncoder,
JwtService jwtService
) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.jwtService = jwtService;
}
public LoginResponse login(LoginRequest request) {
UserEntity user = userRepository.findByUsername(request.username())
.orElseThrow(InvalidCredentialsException::new);
if (!user.isEnabled()) {
throw new InvalidCredentialsException();
}
boolean valid = passwordEncoder.matches(
request.password(),
user.getPasswordHash()
);
if (!valid) {
throw new InvalidCredentialsException();
}
String accessToken = jwtService.generateToken(user);
return new LoginResponse(
accessToken,
"Bearer",
user.getId(),
user.getClient().getId(),
user.getClient().getName(),
user.getUsername(),
user.getRole()
);
}
}
@@ -0,0 +1,12 @@
package com.litoralregas.backend_gateway.auth;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public class InvalidCredentialsException extends RuntimeException {
public InvalidCredentialsException() {
super("Invalid credentials");
}
}
@@ -0,0 +1,7 @@
package com.litoralregas.backend_gateway.auth.dto;
public record LoginRequest(
String username,
String password
) {
}
@@ -0,0 +1,14 @@
package com.litoralregas.backend_gateway.auth.dto;
import com.litoralregas.backend_gateway.user.UserRole;
public record LoginResponse(
String accessToken,
String tokenType,
Long userId,
Long clientId,
String clientName,
String username,
UserRole role
) {
}
@@ -0,0 +1,17 @@
package com.litoralregas.backend_gateway.auth;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class AuthExceptionHandler {
@ExceptionHandler(InvalidCredentialsException.class)
public ResponseEntity<String> handleInvalidCredentials() {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body("Invalid credentials");
}
}
@@ -0,0 +1,26 @@
package com.litoralregas.backend_gateway.security;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
private String secret;
private long expirationMinutes;
public String getSecret() {
return secret;
}
public long getExpirationMinutes() {
return expirationMinutes;
}
public void setSecret(String secret) {
this.secret = secret;
}
public void setExpirationMinutes(long expirationMinutes) {
this.expirationMinutes = expirationMinutes;
}
}
@@ -0,0 +1,42 @@
package com.litoralregas.backend_gateway.security;
import com.litoralregas.backend_gateway.user.UserEntity;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Date;
@Service
public class JwtService {
private final JwtProperties jwtProperties;
public JwtService(JwtProperties jwtProperties) {
this.jwtProperties = jwtProperties;
}
public String generateToken(UserEntity user) {
Instant now = Instant.now();
Instant expiresAt = now.plusSeconds(jwtProperties.getExpirationMinutes() * 60);
return Jwts.builder()
.subject(user.getUsername())
.claim("userId", user.getId())
.claim("clientId", user.getClient().getId())
.claim("role", user.getRole().name())
.issuedAt(Date.from(now))
.expiration(Date.from(expiresAt))
.signWith(getSigningKey())
.compact();
}
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(
jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8)
);
}
}
+4
View File
@@ -24,3 +24,7 @@ gateway:
backend-base-url: http://10.100.1.2:18450
connect-timeout: 3s
response-timeout: 10s
jwt:
secret: your-super-long-secret-key-change-me
expiration-minutes: 1440