You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digital-rmb-backend/src/main/java/com/yau/digitalrmb/security/config/SecurityConfig.java

79 lines
3.9 KiB
Java

package com.yau.digitalrmb.security.config;
import com.nimbusds.jose.jwk.source.ImmutableSecret;
import com.nimbusds.jose.proc.SecurityContext;
import com.yau.digitalrmb.shared.api.ApiResponse;
import com.yau.digitalrmb.shared.api.ErrorCode;
import com.yau.digitalrmb.shared.web.TraceIdFilter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
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 org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@Configuration
@EnableWebSecurity
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtEncoder jwtEncoder(SecurityProperties properties) {
return new NimbusJwtEncoder(new ImmutableSecret<SecurityContext>(secretKey(properties)));
}
@Bean
public JwtDecoder jwtDecoder(SecurityProperties properties) {
return NimbusJwtDecoder.withSecretKey(secretKey(properties)).macAlgorithm(MacAlgorithm.HS256).build();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/actuator/health", "/api/v1/auth/login", "/api/v1/auth/sso",
"/api/v1/auth/cas/**", "/api/v1/auth/session/exchange",
"/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html")
.permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(resourceServer -> resourceServer.jwt(jwt -> { }))
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint((request, response, exception) -> writeError(response, ErrorCode.UNAUTHORIZED, 401))
.accessDeniedHandler((request, response, exception) -> writeError(response, ErrorCode.FORBIDDEN, 403)))
.build();
}
private static SecretKeySpec secretKey(SecurityProperties properties) {
return new SecretKeySpec(properties.getJwt().getSecret().getBytes(StandardCharsets.UTF_8), "HmacSHA256");
}
private static void writeError(jakarta.servlet.http.HttpServletResponse response, ErrorCode code, int status)
throws java.io.IOException {
response.setStatus(status);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
String traceId = response.getHeader(TraceIdFilter.HEADER_NAME);
if (traceId == null) traceId = UUID.randomUUID().toString();
response.getWriter().write("{\"code\":\"" + code.name() + "\",\"message\":\"" + code.name()
+ "\",\"data\":null,\"traceId\":\"" + traceId + "\"}");
}
}