JBear2/src/main/kotlin/at/eisibaer/jbear2/security/SecurityConfiguration.kt

126 lines
5.7 KiB
Kotlin
Raw Normal View History

2024-08-16 10:50:57 +02:00
package at.eisibaer.jbear2.security
import at.eisibaer.jbear2.security.jwt.AuthTokenFilter
import jakarta.servlet.FilterChain
import jakarta.servlet.ServletException
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
import org.springframework.security.web.csrf.*
import org.springframework.util.StringUtils
import org.springframework.web.filter.OncePerRequestFilter
import java.io.IOException
import java.util.function.Supplier
@Configuration
@EnableMethodSecurity
class SecurityConfiguration(
private val userDetailService: UserDetailsService,
private val unauthorizedHandler: AuthTokenFilter,
) {
final val log: Logger = LoggerFactory.getLogger(SecurityConfiguration::class.java);
@Bean
fun securityFilterChain(httpSecurity: HttpSecurity): SecurityFilterChain {
return httpSecurity
.csrf { config ->
config
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(SpaCsrfTokenRequestHandler())
}
.addFilterAfter(CsrfCookieFilter(), BasicAuthenticationFilter::class.java)
.authorizeHttpRequests { config ->
config
.requestMatchers("/api/auth/*").permitAll()
.requestMatchers("/api/**").authenticated()
.requestMatchers("/profile").authenticated()
.requestMatchers("/**").permitAll()
}
.sessionManagement { config: SessionManagementConfigurer<HttpSecurity?> ->
config.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
.authenticationProvider(authenticationProvider())
.addFilterBefore(unauthorizedHandler, UsernamePasswordAuthenticationFilter::class.java)
.build()
}
class SpaCsrfTokenRequestHandler : CsrfTokenRequestAttributeHandler() {
private val delegate: CsrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
override fun handle(request: HttpServletRequest, response: HttpServletResponse, csrfToken: Supplier<CsrfToken>) {
/*
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
* the CsrfToken when it is rendered in the response body.
*/
delegate.handle(request, response, csrfToken)
}
override fun resolveCsrfTokenValue(request: HttpServletRequest, csrfToken: CsrfToken): String? {
/*
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
* to resolve the CsrfToken. This applies when a single-page application includes
* the header value automatically, which was obtained via a cookie containing the
* raw CsrfToken.
*/
return if (StringUtils.hasText(request.getHeader(csrfToken.headerName))) {
super.resolveCsrfTokenValue(request, csrfToken)
} else {
/*
* In all other cases (e.g. if the request contains a request parameter), use
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
* when a server-side rendered form includes the _csrf request parameter as a
* hidden input.
*/
delegate.resolveCsrfTokenValue(request, csrfToken)
}
}
}
class CsrfCookieFilter : OncePerRequestFilter() {
@Throws(ServletException::class, IOException::class)
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
val csrfToken = request.getAttribute("_csrf") as CsrfToken
// Render the token value to a cookie by causing the deferred token to be loaded
csrfToken.token
filterChain.doFilter(request, response)
}
}
@Bean
fun passwordEncoder() : PasswordEncoder {
return Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
}
@Bean
fun authenticationProvider(): DaoAuthenticationProvider{
val authProvider: DaoAuthenticationProvider = DaoAuthenticationProvider()
authProvider.setUserDetailsService(userDetailService)
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
fun authenticationManager(authConfig: AuthenticationConfiguration): AuthenticationManager{
return authConfig.authenticationManager;
}
}