126 lines
5.7 KiB
Kotlin
126 lines
5.7 KiB
Kotlin
|
|
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;
|
||
|
|
}
|
||
|
|
}
|