Implementation of login endpoint.

This commit is contained in:
Florian THIERRY
2023-11-30 10:47:59 +01:00
parent 914785a29b
commit 36a7aacec7
13 changed files with 134 additions and 8 deletions

View File

@@ -33,5 +33,9 @@
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -12,6 +12,9 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import static jakarta.servlet.DispatcherType.FORWARD;
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.http.HttpServletResponse;
@@ -26,7 +29,7 @@ public class SecurityConfiguration {
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(Customizer.withDefaults())
.authorizeHttpRequests(requests -> requests
.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
.dispatcherTypeMatchers(FORWARD).permitAll()
.requestMatchers(
HttpMethod.GET,
"/api/health/check"
@@ -39,10 +42,10 @@ public class SecurityConfiguration {
)
.exceptionHandling(configurer -> configurer
.defaultAuthenticationEntryPointFor(
(request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED),
(request, response, authException) -> response.sendError(SC_UNAUTHORIZED),
new AntPathRequestMatcher("/api/**")
).defaultAccessDeniedHandlerFor(
(request, response, accessDeniedException) -> response.sendError(HttpServletResponse.SC_FORBIDDEN),
(request, response, accessDeniedException) -> response.sendError(SC_FORBIDDEN),
new AntPathRequestMatcher("/api/**")
)
);

View File

@@ -1,7 +1,9 @@
package org.sportshub.application.user;
package org.sportshub.application.security;
import java.util.UUID;
import org.sportshub.application.security.model.CustomUserDetails;
import org.sportshub.application.user.UserUseCases;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

View File

@@ -0,0 +1,46 @@
package org.sportshub.application.security;
import java.time.ZonedDateTime;
import org.sportshub.domain.user.model.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
@Service
public class JwtService {
private final Algorithm algorithm;
private final JWTVerifier jwtVerifier;
public JwtService(@Value("${application.security.secretKey}") String secretKey) {
algorithm = Algorithm.HMAC512(secretKey);
jwtVerifier = JWT.require(algorithm).build();
}
public String createJwt(User user) {
ZonedDateTime expirationDate = ZonedDateTime.now().plusMinutes(30);
return JWT.create()
.withSubject(user.id().toString())
.withExpiresAt(expirationDate.toInstant())
.sign(algorithm);
}
public boolean isValid(String token) {
boolean result;
try {
jwtVerifier.verify(token);
result = true;
} catch (JWTVerificationException exception) {
result = false;
}
return result;
}
public String extractUsername(String token) {
return JWT.decode(token).getSubject();
}
}

View File

@@ -1,4 +1,4 @@
package org.sportshub.application.user;
package org.sportshub.application.security.model;
import static java.util.Collections.emptyList;
import java.util.Collection;

View File

@@ -4,15 +4,26 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.sportshub.application.security.JwtService;
import org.sportshub.domain.exception.LoginFailureException;
import org.sportshub.domain.user.model.User;
import org.sportshub.domain.user.port.UserPort;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserUseCases {
private final PasswordEncoder passwordEncoder;
private final JwtService jwtService;
private final UserPort userPort;
public UserUseCases(final UserPort userPort) {
public UserUseCases(
PasswordEncoder passwordEncoder,
JwtService jwtService,
UserPort userPort
) {
this.passwordEncoder = passwordEncoder;
this.jwtService = jwtService;
this.userPort = userPort;
}
@@ -23,4 +34,11 @@ public class UserUseCases {
public List<User> findAll() {
return userPort.findAll();
}
public String authenticate(final UUID id, final String password) {
return userPort.findById(id)
.filter(user -> passwordEncoder.matches(password, user.password()))
.map(jwtService::createJwt)
.orElseThrow(LoginFailureException::new);
}
}