Cleanup
Some checks failed
Gitea/swiss-backend/pipeline/head There was a failure building this commit
Some checks failed
Gitea/swiss-backend/pipeline/head There was a failure building this commit
This commit is contained in:
@@ -1,46 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
|
||||||
import org.springframework.context.annotation.Profile;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@ConditionalOnExpression("${security}")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CrossOrigin
|
|
||||||
public class AuthenticationController {
|
|
||||||
|
|
||||||
private final JwtUserDetailsService jwtUserDetailsService;
|
|
||||||
|
|
||||||
private final AuthenticationManager authenticationManager;
|
|
||||||
|
|
||||||
private final JwtTokenService jwtTokenService;
|
|
||||||
|
|
||||||
@PostMapping("/authenticate")
|
|
||||||
public AuthenticationResponse authenticate(@RequestBody @Valid final AuthenticationRequest authenticationRequest) {
|
|
||||||
try {
|
|
||||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
|
|
||||||
authenticationRequest.getUsername(), authenticationRequest.getPassword()));
|
|
||||||
} catch (final BadCredentialsException ex) {
|
|
||||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
final UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(authenticationRequest.getUsername());
|
|
||||||
final AuthenticationResponse authenticationResponse = new AuthenticationResponse();
|
|
||||||
authenticationResponse.setAccessToken(jwtTokenService.generateToken(userDetails));
|
|
||||||
authenticationResponse.setUsername(authenticationRequest.getUsername());
|
|
||||||
return authenticationResponse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import jakarta.validation.constraints.Size;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
public class AuthenticationRequest {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Size(max = 255)
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Size(max = 255)
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
public class AuthenticationResponse {
|
|
||||||
|
|
||||||
private String accessToken;
|
|
||||||
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
public class Client {
|
|
||||||
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String hash;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
import org.springframework.util.DigestUtils;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
public class ClientRepository {
|
|
||||||
|
|
||||||
private static final String hash = "$2a$12$FjsFqFTorg0sXCiSISFS3.xvSCzmAATIcA7wh5w8WtQ7eYZC.H4UW";
|
|
||||||
|
|
||||||
Optional<Client> findByLogin(String username) {
|
|
||||||
if (username.equals("bcholten")) {
|
|
||||||
var client = new Client();
|
|
||||||
client.setId(1L);
|
|
||||||
client.setHash(hash);
|
|
||||||
return Optional.of(client);
|
|
||||||
} else {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
|
||||||
import org.springframework.context.annotation.Profile;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@ConditionalOnExpression("${security}")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class JwtRequestFilter extends OncePerRequestFilter {
|
|
||||||
|
|
||||||
private final JwtTokenService jwtTokenService;
|
|
||||||
|
|
||||||
private final JwtUserDetailsService jwtUserDetailsService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
|
|
||||||
final FilterChain chain) throws IOException, ServletException {
|
|
||||||
// look for Bearer auth header
|
|
||||||
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
|
|
||||||
if (header == null || !header.startsWith("Bearer ")) {
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String token = header.substring(7);
|
|
||||||
final String username = jwtTokenService.validateTokenAndGetUsername(token);
|
|
||||||
if (username == null) {
|
|
||||||
// validation failed or token expired
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set user details on spring security context
|
|
||||||
final JwtUserDetails userDetails = jwtUserDetailsService.loadUserByUsername(username);
|
|
||||||
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
|
||||||
userDetails, null, userDetails.getAuthorities());
|
|
||||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
|
||||||
|
|
||||||
// continue with authenticated user
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
|
||||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
|
||||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
|
|
||||||
import static org.springframework.security.config.Customizer.withDefaults;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@ConditionalOnExpression("${security}")
|
|
||||||
@EnableWebSecurity
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class JwtSecurityConfig {
|
|
||||||
|
|
||||||
private final JwtRequestFilter jwtRequestFilter;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
BCryptPasswordEncoder passwordEncoder() {
|
|
||||||
return new BCryptPasswordEncoder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public AuthenticationManager authenticationManager(
|
|
||||||
final AuthenticationConfiguration authenticationConfiguration) throws Exception {
|
|
||||||
return authenticationConfiguration.getAuthenticationManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityFilterChain configure(final HttpSecurity http) throws Exception {
|
|
||||||
return http.cors(withDefaults())
|
|
||||||
.csrf((csrf) -> csrf.disable())
|
|
||||||
.authorizeHttpRequests((authorize) -> authorize
|
|
||||||
.requestMatchers("/", "/authenticate", "/testdata").permitAll()
|
|
||||||
.anyRequest().hasAuthority(UserRoles.ROLE_USER))
|
|
||||||
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
|
||||||
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public WebMvcConfigurer corsConfigurer() {
|
|
||||||
return new WebMvcConfigurer() {
|
|
||||||
@Override
|
|
||||||
public void addCorsMappings(CorsRegistry registry) {
|
|
||||||
registry.addMapping("/**")
|
|
||||||
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import com.auth0.jwt.JWT;
|
|
||||||
import com.auth0.jwt.JWTVerifier;
|
|
||||||
import com.auth0.jwt.algorithms.Algorithm;
|
|
||||||
import com.auth0.jwt.exceptions.JWTVerificationException;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
|
||||||
import java.security.interfaces.RSAPublicKey;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@ConditionalOnExpression("${security}")
|
|
||||||
public class JwtTokenService {
|
|
||||||
|
|
||||||
private static final Duration JWT_TOKEN_VALIDITY = Duration.ofDays(7);
|
|
||||||
|
|
||||||
private final Algorithm rsa256;
|
|
||||||
private final JWTVerifier verifier;
|
|
||||||
|
|
||||||
public JwtTokenService(@Value("classpath:certs/public.pem") final RSAPublicKey publicKey,
|
|
||||||
@Value("classpath:certs/private.pem") final RSAPrivateKey privateKey) {
|
|
||||||
this.rsa256 = Algorithm.RSA256(publicKey, privateKey);
|
|
||||||
this.verifier = JWT.require(this.rsa256).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String generateToken(final UserDetails userDetails) {
|
|
||||||
final Instant now = Instant.now();
|
|
||||||
return JWT.create()
|
|
||||||
.withSubject(userDetails.getUsername())
|
|
||||||
.withIssuer("app")
|
|
||||||
.withIssuedAt(now)
|
|
||||||
.withExpiresAt(now.plusMillis(JWT_TOKEN_VALIDITY.toMillis()))
|
|
||||||
.sign(this.rsa256);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String validateTokenAndGetUsername(final String token) {
|
|
||||||
try {
|
|
||||||
return verifier.verify(token).getSubject();
|
|
||||||
} catch (final JWTVerificationException verificationEx) {
|
|
||||||
// log.warn("token invalid: {}", verificationEx.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
import org.springframework.security.core.userdetails.User;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public class JwtUserDetails extends User {
|
|
||||||
|
|
||||||
public final Long id;
|
|
||||||
|
|
||||||
public JwtUserDetails(final Long id, final String username, final String hash,
|
|
||||||
final Collection<? extends GrantedAuthority> authorities) {
|
|
||||||
super(username, hash, authorities);
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class JwtUserDetailsService implements UserDetailsService {
|
|
||||||
|
|
||||||
private final ClientRepository clientRepository;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JwtUserDetails loadUserByUsername(final String username) {
|
|
||||||
final Client client = clientRepository.findByLogin(username).orElseThrow(
|
|
||||||
() -> new UsernameNotFoundException("User " + username + " not found"));
|
|
||||||
final List<SimpleGrantedAuthority> roles = Collections.singletonList(new SimpleGrantedAuthority(UserRoles.ROLE_USER));
|
|
||||||
return new JwtUserDetails(client.getId(), username, client.getHash(), roles);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class LoginCredentialsDto {
|
|
||||||
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
private String ipAddres;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class UserDto {
|
|
||||||
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
private String token;
|
|
||||||
|
|
||||||
private String refreshToken;
|
|
||||||
|
|
||||||
private String ipAddress;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package nl.connectedit.swiss.authentication;
|
|
||||||
|
|
||||||
public class UserRoles {
|
|
||||||
|
|
||||||
public static final String USER = "USER";
|
|
||||||
public static final String ROLE_USER = "ROLE_" + USER;
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user