Authorization working

This commit is contained in:
2025-02-22 16:54:37 -05:00
parent 96419f0077
commit 02c615ee0c
47 changed files with 1894 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
package com.mattrixwv.raidbuilder.config;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "rsa")
public record RsaKeyProperties(RSAPublicKey publicKey, RSAPrivateKey privateKey){
}

View File

@@ -0,0 +1,72 @@
package com.mattrixwv.raidbuilder.config;
import org.springframework.context.annotation.Bean;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
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.web.SecurityFilterChain;
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 lombok.RequiredArgsConstructor;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig{
private final RsaKeyProperties rsaKeys;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> {
auth.requestMatchers("/auth/refresh").permitAll() //Permit refresh tokens
.requestMatchers(HttpMethod.POST, "/auth/signup", "/auth/confirm").permitAll() //Permit signup operations
.requestMatchers("/auth/forgot", "/auth/forgot/*").permitAll() //Permit forgot password operations
.anyRequest().authenticated();
})
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.httpBasic(Customizer.withDefaults())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
@Bean
public JwtEncoder jwtEncoder(){
JWK jwk = new RSAKey.Builder(rsaKeys.publicKey()).privateKey(rsaKeys.privateKey()).build();
JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jwks);
}
@Bean
public JwtDecoder jwtDecoder(){
return NimbusJwtDecoder.withPublicKey(rsaKeys.publicKey()).build();
}
}

View File

@@ -0,0 +1,50 @@
package com.mattrixwv.raidbuilder.config;
import java.time.Duration;
import java.time.Instant;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
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 com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.service.AccountPermissionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Configuration
@RequiredArgsConstructor
public class TokenService{
private final JwtEncoder encoder;
private final AccountPermissionService accountPermissionService;
//Fields
@Value("${jwt.accessTokenDuration}")
private Duration accessTokenDuration;
public String generateAccessToken(Account account){
log.debug("Generating access token for account {}", account.getAccountId());
String scope = accountPermissionService.getByAccountId(account.getAccountId()).stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(" "));
Instant now = Instant.now();
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("self")
.issuedAt(now)
.expiresAt(now.plus(accessTokenDuration))
.subject(account.getUsername())
.claim("scope", scope)
.build();
return encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}
}

View File

@@ -0,0 +1,60 @@
package com.mattrixwv.raidbuilder.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module;
import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module.Feature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer{
@Value("${allowedOrigins}")
private String allowedOrigins;
@Override
public void addCorsMappings(@NonNull CorsRegistry registry){
log.debug("Adding CORS mappings: {}", allowedOrigins);
registry.addMapping("/**")
.allowedOriginPatterns(allowedOrigins)
.allowedMethods("GET", "PUT", "DELETE", "OPTIONS", "PATCH", "POST");
}
@Bean
public ObjectMapper objectMapper(){
ObjectMapper mapper = new ObjectMapper();
log.debug("Starting mapping configuration");
//Make sure Jackson doesn't attempt lazy loading
Hibernate6Module hibernate6Module = new Hibernate6Module();
hibernate6Module.configure(Feature.FORCE_LAZY_LOADING, false);
hibernate6Module.configure(Feature.USE_TRANSIENT_ANNOTATION, false);
hibernate6Module.configure(Feature.REQUIRE_EXPLICIT_LAZY_LOADING_MARKER, true);
hibernate6Module.configure(Feature.WRITE_MISSING_ENTITIES_AS_NULL, false);
mapper.registerModule(hibernate6Module);
//Print dates as strings
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.findAndRegisterModules();
log.debug("Completed mapping configuration");
return mapper;
}
}

View File

@@ -0,0 +1,74 @@
package com.mattrixwv.raidbuilder.config;
import java.io.IOException;
import java.util.StringJoiner;
import org.slf4j.MDC;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class WebFilter extends OncePerRequestFilter{
@Override
public void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException{
if(!request.getMethod().equalsIgnoreCase("OPTIONS")){
setupMDC(request);
}
//Continue to the controller
filterChain.doFilter(request, response);
//Clear the MDC for the next request
MDC.clear();
}
private void setupMDC(HttpServletRequest request){
//Get the requestId
String requestId = request.getHeader("X-Request-Id");
if(requestId != null){
MDC.put("requestId", requestId);
}
//Get IP address
String forwardedFor = request.getHeader("X-Forwarded-For");
if(forwardedFor != null){
MDC.put("ip", forwardedFor.split(",")[0]);
}
//Get all of the parameters in the request and print them in the log
StringJoiner parameters = new StringJoiner(", ");
request.getParameterMap().entrySet().forEach(entry -> {
if(!entry.getKey().equals("_")){
String key = entry.getKey();
String value = "";
if(entry.getValue().length > 1){
StringJoiner joiner = new StringJoiner(", ", "[", "]");
for(String str : entry.getValue()){
joiner.add(str);
}
value = joiner.toString();
}
else{
value = entry.getValue()[0];
}
parameters.add(key + "->" + value);
}
});
if(parameters.length() > 0){
log.info("Request parameters: {}", parameters);
}
//Get the path
MDC.put("url", request.getRequestURI());
}
}