Updated auth config
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:
4
pom.xml
4
pom.xml
@@ -31,6 +31,10 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-security</artifactId>
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.auth0</groupId>
|
<groupId>com.auth0</groupId>
|
||||||
<artifactId>java-jwt</artifactId>
|
<artifactId>java-jwt</artifactId>
|
||||||
|
|||||||
44
src/main/java/nl/connectedit/swiss/auth/AuthController.java
Normal file
44
src/main/java/nl/connectedit/swiss/auth/AuthController.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package nl.connectedit.swiss.auth;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
|
||||||
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@CrossOrigin
|
||||||
|
@RequestMapping("/api/auth")
|
||||||
|
public class AuthController {
|
||||||
|
|
||||||
|
private final JwtEncoder encoder;
|
||||||
|
|
||||||
|
public AuthController(JwtEncoder encoder) {
|
||||||
|
this.encoder = encoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public String auth(Authentication authentication) {
|
||||||
|
Instant now = Instant.now();
|
||||||
|
long expiry = 36000L;
|
||||||
|
String scope = authentication.getAuthorities().stream()
|
||||||
|
.map(GrantedAuthority::getAuthority)
|
||||||
|
.collect(Collectors.joining(","));
|
||||||
|
JwtClaimsSet claims = JwtClaimsSet.builder()
|
||||||
|
.issuer("self")
|
||||||
|
.issuedAt(now)
|
||||||
|
.expiresAt(now.plusSeconds(expiry))
|
||||||
|
.subject(authentication.getName())
|
||||||
|
.claim("scope", scope)
|
||||||
|
.build();
|
||||||
|
return this.encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
9
src/main/java/nl/connectedit/swiss/auth/Roles.java
Normal file
9
src/main/java/nl/connectedit/swiss/auth/Roles.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package nl.connectedit.swiss.auth;
|
||||||
|
|
||||||
|
public final class Roles {
|
||||||
|
|
||||||
|
public static final String USER = "USER";
|
||||||
|
|
||||||
|
private Roles() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +1,148 @@
|
|||||||
package nl.connectedit.swiss.config;
|
package nl.connectedit.swiss.config;
|
||||||
|
|
||||||
|
import com.nimbusds.jose.jwk.JWK;
|
||||||
|
import com.nimbusds.jose.jwk.JWKSet;
|
||||||
|
import com.nimbusds.jose.jwk.RSAKey;
|
||||||
|
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
|
||||||
|
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||||
|
import com.nimbusds.jose.proc.SecurityContext;
|
||||||
import jakarta.servlet.DispatcherType;
|
import jakarta.servlet.DispatcherType;
|
||||||
|
import nl.connectedit.swiss.auth.Roles;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||||
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
|
||||||
//@Configuration
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
//@EnableWebSecurity
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
// @Bean
|
private final RSAPublicKey key;
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
private final RSAPrivateKey priv;
|
||||||
|
|
||||||
|
public SecurityConfig(@Value("${jwt.public.key}") RSAPublicKey key,
|
||||||
|
@Value("${jwt.private.key}") RSAPrivateKey priv) {
|
||||||
|
this.key = key;
|
||||||
|
this.priv = priv;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
||||||
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
|
.csrf(csrf -> csrf.ignoringRequestMatchers("/api/auth"))
|
||||||
// .authorizeHttpRequests(request -> {
|
.authorizeHttpRequests(authorize -> authorize
|
||||||
// request.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll();
|
// Allow OPTIONS requests for CORS preflight
|
||||||
// request.requestMatchers("/error").permitAll();
|
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
||||||
// });
|
// Allow POST to /api/auth without JWT (Basic Auth will still be required)
|
||||||
|
.requestMatchers(HttpMethod.POST, "/api/auth").permitAll()
|
||||||
|
// All other requests require authentication
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.httpBasic(Customizer.withDefaults())
|
||||||
|
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
|
||||||
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
|
.exceptionHandling(exceptions -> exceptions
|
||||||
|
.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
|
||||||
|
.accessDeniedHandler(new BearerTokenAccessDeniedHandler())
|
||||||
|
);
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CorsConfigurationSource corsConfigurationSource() {
|
||||||
|
CorsConfiguration configuration = new CorsConfiguration();
|
||||||
|
|
||||||
|
// Allow your Angular app's origin(s)
|
||||||
|
configuration.setAllowedOrigins(Arrays.asList(
|
||||||
|
"http://localhost:4200",
|
||||||
|
"http://localhost:8080",
|
||||||
|
// Add your production URL here when deploying
|
||||||
|
"https://badminton-toernooi.nl",
|
||||||
|
"https://test.badminton-toernooi.nl"
|
||||||
|
));
|
||||||
|
|
||||||
|
configuration.setAllowedMethods(Arrays.asList(
|
||||||
|
"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"
|
||||||
|
));
|
||||||
|
|
||||||
|
configuration.setAllowedHeaders(Arrays.asList(
|
||||||
|
"Authorization",
|
||||||
|
"Content-Type",
|
||||||
|
"Accept"
|
||||||
|
));
|
||||||
|
|
||||||
|
configuration.setAllowCredentials(true);
|
||||||
|
configuration.setMaxAge(3600L);
|
||||||
|
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
source.registerCorsConfiguration("/**", configuration);
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
UserDetailsService users() {
|
||||||
|
return new InMemoryUserDetailsManager(
|
||||||
|
User.withUsername("bcholten")
|
||||||
|
.password(passwordEncoder().encode("bcholten"))
|
||||||
|
.roles(Roles.USER)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
JwtDecoder jwtDecoder() {
|
||||||
|
return NimbusJwtDecoder.withPublicKey(this.key).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
JwtEncoder jwtEncoder() {
|
||||||
|
JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
|
||||||
|
JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
|
||||||
|
return new NimbusJwtEncoder(jwks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtAuthenticationConverter jwtAuthenticationConverter() {
|
||||||
|
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
|
// Remove the SCOPE_ prefix
|
||||||
|
grantedAuthoritiesConverter.setAuthorityPrefix("");
|
||||||
|
|
||||||
|
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
|
||||||
|
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
|
||||||
|
return jwtAuthenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package nl.connectedit.swiss.controller;
|
package nl.connectedit.swiss.controller;
|
||||||
|
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import nl.connectedit.swiss.auth.Roles;
|
||||||
import nl.connectedit.swiss.domain.entity.Player;
|
import nl.connectedit.swiss.domain.entity.Player;
|
||||||
import nl.connectedit.swiss.dto.PlayerDto;
|
import nl.connectedit.swiss.dto.PlayerDto;
|
||||||
import nl.connectedit.swiss.mapper.PlayerMapper;
|
import nl.connectedit.swiss.mapper.PlayerMapper;
|
||||||
@@ -12,6 +14,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
|
@RolesAllowed(Roles.USER)
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class PlayerController {
|
public class PlayerController {
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package nl.connectedit.swiss.controller;
|
package nl.connectedit.swiss.controller;
|
||||||
|
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import nl.connectedit.swiss.auth.Roles;
|
||||||
import nl.connectedit.swiss.domain.TournamentStatus;
|
import nl.connectedit.swiss.domain.TournamentStatus;
|
||||||
import nl.connectedit.swiss.dto.TournamentRegistrationDto;
|
import nl.connectedit.swiss.dto.TournamentRegistrationDto;
|
||||||
import nl.connectedit.swiss.mapper.TournamentPlayerRegistrationMapper;
|
import nl.connectedit.swiss.mapper.TournamentPlayerRegistrationMapper;
|
||||||
@@ -20,6 +22,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
|
@RolesAllowed(Roles.USER)
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class RegistrationController {
|
public class RegistrationController {
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package nl.connectedit.swiss.controller;
|
|||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import nl.connectedit.swiss.auth.Roles;
|
||||||
import nl.connectedit.swiss.domain.*;
|
import nl.connectedit.swiss.domain.*;
|
||||||
import nl.connectedit.swiss.domain.entity.Player;
|
import nl.connectedit.swiss.domain.entity.Player;
|
||||||
import nl.connectedit.swiss.domain.entity.Registration;
|
import nl.connectedit.swiss.domain.entity.Registration;
|
||||||
@@ -39,6 +41,7 @@ import static nl.connectedit.swiss.domain.PlayerStrength.*;
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
|
@RolesAllowed(Roles.USER)
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class TestController {
|
public class TestController {
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package nl.connectedit.swiss.controller;
|
package nl.connectedit.swiss.controller;
|
||||||
|
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import nl.connectedit.swiss.auth.Roles;
|
||||||
import nl.connectedit.swiss.domain.TournamentStatus;
|
import nl.connectedit.swiss.domain.TournamentStatus;
|
||||||
import nl.connectedit.swiss.domain.entity.Tournament;
|
import nl.connectedit.swiss.domain.entity.Tournament;
|
||||||
import nl.connectedit.swiss.dto.ResultDto;
|
import nl.connectedit.swiss.dto.ResultDto;
|
||||||
@@ -20,6 +22,7 @@ import java.util.Objects;
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
|
@RolesAllowed(Roles.USER)
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class TournamentController {
|
public class TournamentController {
|
||||||
|
|
||||||
|
|||||||
@@ -30,4 +30,10 @@ management:
|
|||||||
probes:
|
probes:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
security: true
|
security: true
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
private:
|
||||||
|
key: classpath:certs/private.pem
|
||||||
|
public:
|
||||||
|
key: classpath:certs/public.pem
|
||||||
|
|||||||
Reference in New Issue
Block a user