Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Error creating bean with name 'securityConfiguration' defined in file

To tentando subir o projeto, mas um erro de construtor vazio ta me impedindo de prosseguir.

package com.group.solution.config

import com.group.solution.security.JWTAuthenticationFilter
import com.group.solution.security.JWTLoginFilter
import com.group.solution.security.JWTUtil
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
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.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

@Configuration
@EnableWebSecurity
class SecurityConfiguration (
    private val configuration: AuthenticationConfiguration,
    private val userDetailsService: UserDetailsService,
    private val jwtUtil: JWTUtil,
    val constant: String = "/users"
) {

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http.csrf { it.disable() }
            .authorizeHttpRequests { auth ->
                auth
                    .requestMatchers("/h2-console/**").permitAll()
                    .requestMatchers(HttpMethod.POST, "/login").permitAll()
                    .requestMatchers(HttpMethod.POST, constant).permitAll()
                    .requestMatchers(HttpMethod.DELETE, constant).hasRole("ADMIN")
                    .requestMatchers(HttpMethod.GET, constant).hasRole("ADMIN")
                    .requestMatchers(HttpMethod.PUT, constant).hasAnyRole("ADMIN", "USER")
                    .anyRequest().authenticated()
            }
            .addFilterBefore(
                JWTLoginFilter(
                    authenticationManager = configuration.authenticationManager,
                    jwtUtil = jwtUtil
                ), UsernamePasswordAuthenticationFilter().javaClass
            )
            .addFilterBefore(
                JWTAuthenticationFilter(jwtUtil = jwtUtil),
                UsernamePasswordAuthenticationFilter::class.java
            )
            .headers { headers ->
                headers.frameOptions().disable()
            }
            .sessionManagement {
                it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            }
        return http.build()
    }

    @Bean
    fun encoder(): PasswordEncoder = BCryptPasswordEncoder()

    fun configure(auth: AuthenticationManagerBuilder?) {
        auth?.userDetailsService(userDetailsService)?.passwordEncoder(encoder())
    }
}

erro: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfiguration' defined in file [C:\Users\BRAGA\Desktop\Sales-system\solution\core\target\classes\com\group\solution\config\SecurityConfiguration.class]: Failed to instantiate [com.group.solution.config.SecurityConfiguration$$SpringCGLIB$$0]: No default constructor found at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1337) ~[spring-beans-6.1.12.jar:6.1.12]

6 respostas

Outas classes

package com.group.solution.security

import com.group.solution.domain.service.UserAuthenticatorService
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.security.Keys
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.GrantedAuthority
import org.springframework.stereotype.Component
import java.nio.charset.StandardCharsets
import java.util.*

@Component
@ConfigurationProperties(prefix = "jwt")
class JWTUtil(
    private val userAuthenticatorService: UserAuthenticatorService
) {

    @Value("\${jwt.secret}")
    private var secret: String = "2a12Dpr9yBjZksrrC34hnQEG1uDyF5HKckz3Cob4j5md1Jl3jXPF1ejzi"

    private val expiration: Long = 60000

    private val key = Keys.hmacShaKeyFor(secret.toByteArray(StandardCharsets.UTF_8))

    fun generateToken(username: String, authorities: MutableCollection<out GrantedAuthority>): String {
        return Jwts
            .builder()
            .subject(username)
            .claim("role", authorities)
            .expiration(Date(System.currentTimeMillis() + expiration))
            .signWith(key)
            .compact()
    }

    fun isValid(jwt: String?): Boolean {
        return try {
            Jwts.parser()
                .verifyWith(key)
                .build()
                .parseSignedClaims(jwt)
            true
        } catch (e: IllegalArgumentException) {
            false
        }
    }

    fun getAuthentication(jwt: String?): UsernamePasswordAuthenticationToken {
        val username = Jwts.parser()
            .verifyWith(key)
            .build()
            .parseSignedClaims(jwt)
            .payload
            .subject
        val user = userAuthenticatorService.loadUserByUsername(username.toString())
        return UsernamePasswordAuthenticationToken(username, null, user.authorities)
    }
}
package com.group.solution.security

import com.fasterxml.jackson.databind.ObjectMapper
import com.group.solution.model.dto.Credentials
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

class JWTLoginFilter(
    private val authenticationManager: AuthenticationManager?,
    private val jwtUtil: JWTUtil,
) : UsernamePasswordAuthenticationFilter(authenticationManager) {

    override fun attemptAuthentication(request: HttpServletRequest?, response: HttpServletResponse?): Authentication? {
        val (username,password) =ObjectMapper().readValue(request?.inputStream, Credentials::class.java)
        val token = UsernamePasswordAuthenticationToken(username, password)
        return authenticationManager?.authenticate(token)
    }

    override fun successfulAuthentication(
        request: HttpServletRequest?,
        response: HttpServletResponse?,
        chain: FilterChain?,
        authResult: Authentication?
    ) {
        val user = (authResult?.principal as UserDetails)
        val token = jwtUtil.generateToken(user.username, user.authorities)
        response?.addHeader("Authorization", "Bearer $token")
    }
}
package com.group.solution.security

import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.filter.OncePerRequestFilter


class JWTAuthenticationFilter(private val jwtUtil: JWTUtil) : OncePerRequestFilter() {

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        val token = request.getHeader("Authorization")
        val jwt = getTokenDetail(token)
        if (jwtUtil.isValid(jwt)) {
            val authentication = jwtUtil.getAuthentication(jwt)
            SecurityContextHolder.getContext().authentication = authentication
        }
        filterChain.doFilter(request,response)
    }

    private fun getTokenDetail(token: String?): String? {
        return token?.let { jwt ->
            jwt?.startsWith("Bearer")
            jwt.substring(7, jwt.length)
        }
    }
}
package com.group.solution.domain.service

import com.group.solution.domain.service.repository.UserRepository
import com.group.solution.model.dto.LoginRequest
import com.group.solution.model.entities.UserDatail
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.stereotype.Service

@Service
class UserAuthenticatorService ( private val repository: UserRepository) : UserDetailsService{

    override fun loadUserByUsername (username: String?): UserDetails {
        val user = repository.findByEmail(username)?: throw  RuntimeException()

        return UserDatail(
            user,
            user.role
        )
    }
}

Se alguem tiver ainda com problemas, vou tentar explicar um pouco do que fiz para resolver

@Component
class JWTUtil (
    private val userAuthenticationService: UserAuthenticationService
) {

    @Value("\${spring.jwt.secret}")
    private lateinit var secret: String

    private lateinit var key: SecretKey

    @PostConstruct
    fun init() {
        key = Keys.hmacShaKeyFor(secret.toByteArray())
    }

    private val expiration: Long = 60000

    fun generateToken(username: String, authorities: MutableCollection<out GrantedAuthority>): String {
        return Jwts
            .builder()
            .subject(username)
            .claim("role", authorities)
            .expiration(Date(System.currentTimeMillis() + expiration))
            .signWith(key)
            .compact()
    }
    fun isValid(jwt: String?): Boolean {
        return try {
            Jwts.parser()
                .verifyWith(key)
                .build()
                .parseSignedClaims(jwt)
            true
        } catch (e: IllegalArgumentException) {
            false
        }
    }

    fun getAuthentication(jwt: String?): UsernamePasswordAuthenticationToken {
        val username = Jwts.parser()
            .verifyWith(key)
            .build()
            .parseSignedClaims(jwt)
            .payload
            .subject
        val user = userAuthenticationService.loadUserByUsername(username.toString())
        return UsernamePasswordAuthenticationToken(username, null, user.authorities)
    }
}

na classe JWT util, eu troquei o tipo de chave que o Key recebia e os metodos verifyWith compilaram tranquilamente.

solução!

Em seguida, tive um problema com injecao de dependencias (injecao de dependencias circulares). Basicamente, uma classe minha chamava um bean que dependia de outro que ainda nao tinha sido inicializado.

package com.group.solution.config

import com.group.solution.domain.service.UserAuthenticationService
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder

@Configuration
class ConfigurationBeans (private val userAuthenticationService: UserAuthenticationService) {

    @Bean
    fun bCryptPasswordEncoder(): PasswordEncoder = BCryptPasswordEncoder()

    @Bean
    fun authenticationManager(authenticationConfiguration: AuthenticationConfiguration): AuthenticationManager {
        return authenticationConfiguration.authenticationManager
    }

    fun configure(auth: AuthenticationManagerBuilder?) {
        auth?.userDetailsService(userAuthenticationService)?.passwordEncoder(bCryptPasswordEncoder())
    }
}

Entao eu criei uma outra classe para definir os beans. Na minha aplicacao, eu fiz um crud basico, difere do projeto do curso. Nao fiz um front, entao o login foi feito via postman mesmo em endpoint /login, normal. Depois disso o resto funcionou certinho.