biblenotes-java/src/main/java/de/w665/biblenotes/service/AuthenticationService.java
Max W. a8411b6e63 update auth
- add tables and repos
- add dto
2025-01-19 19:31:43 +01:00

123 lines
4.6 KiB
Java

package de.w665.biblenotes.service;
import de.w665.biblenotes.db.repo.UserLoginRepository;
import de.w665.biblenotes.db.repo.UserRepository;
import de.w665.biblenotes.db.entity.User;
import de.w665.biblenotes.db.entity.UserLogin;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.Date;
import java.util.Optional;
@Slf4j
@Service
public class AuthenticationService {
private final UserRepository userRepository;
private final UserLoginRepository userLoginRepository;
@Value("${secureapi.jwt.secret}")
private String secretString;
@Value("${secureapi.jwt.expiration}")
private long expirationTime; // in milliseconds
private SecretKey secretKey;
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
public AuthenticationService(UserRepository userRepository, UserLoginRepository userLoginRepository) {
this.userRepository = userRepository;
this.userLoginRepository = userLoginRepository;
}
@PostConstruct
public void init() {
log.debug("Initializing secret key");
byte[] encodedKey = Base64.getEncoder().encode(secretString.getBytes()); // encode the secret key
this.secretKey = Keys.hmacShaKeyFor(encodedKey);
}
public String authenticate(String username, String password, String remoteAddr, long... expirationTime/*FOR TESTING VALIDITY*/) {
if(expirationTime.length > 0) {
this.expirationTime = expirationTime[0];
}
log.debug("Authenticating user: {}", username);
Optional<User> optionalUser = userRepository.findByUsername(username);
if (optionalUser.isPresent() && passwordEncoder.matches(password, optionalUser.get().getPassword())) {
User user = optionalUser.get();
UserLogin userLogin = new UserLogin();
userLogin.setUserId(user.getId());
userLogin.setLoginTime(LocalDateTime.now());
userLogin.setLoginIp(remoteAddr);
userLoginRepository.save(userLogin);
userRepository.updateLastLoginForUser(user.getUsername(), LocalDateTime.now());
return generateToken(user);
}
return null;
}
private String generateToken(User user) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
Date expiryDate = new Date(nowMillis + expirationTime);
return Jwts.builder()
.subject("BibleNotes Authentication Token")
.issuedAt(now)
.claim("role", user.getRole())
.claim("username", user.getUsername())
.claim("userId", user.getId())
.expiration(expiryDate)
.signWith(secretKey)
.compact();
}
public boolean validateToken(String token) {
try {
Jwt<?,?> jwt = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token);
Claims claims = (Claims) jwt.getPayload();
return !claims.getExpiration().before(new Date()); // Checks if the token is expired too
} catch (Exception e) {
return false;
}
}
public String extractSubject(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getSubject();
}
/**
* Retrieves a typed claim from the JWT.
* @param token the JWT from which to extract the claim
* @param claimName the name of the claim to retrieve
* @param claimType the Class object of <T> the expected type of the claim value
* @return the value of the specified claim as type T, or null if not found or in case of an error
* Usage example: getClaimValue(token, "role", String.class)
*/
public <T> T getClaimValue(String token, String claimName, Class<T> claimType) {
try {
Jwt<?, ?> jwt = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token);
Claims claims = (Claims) jwt.getPayload();
return claims.get(claimName, claimType);
} catch (Exception e) {
log.error("Error parsing claims from token: ", e);
return null;
}
}
public String encodePassword(String password) {
return passwordEncoder.encode(password);
}
}