CoderFunda
  • Home
  • About us
    • Contact Us
    • Disclaimer
    • Privacy Policy
    • About us
  • Home
  • Php
  • HTML
  • CSS
  • JavaScript
    • JavaScript
    • Jquery
    • JqueryUI
    • Stock
  • SQL
  • Vue.Js
  • Python
  • Wordpress
  • C++
    • C++
    • C
  • Laravel
    • Laravel
      • Overview
      • Namespaces
      • Middleware
      • Routing
      • Configuration
      • Application Structure
      • Installation
    • Overview
  • DBMS
    • DBMS
      • PL/SQL
      • SQLite
      • MongoDB
      • Cassandra
      • MySQL
      • Oracle
      • CouchDB
      • Neo4j
      • DB2
      • Quiz
    • Overview
  • Entertainment
    • TV Series Update
    • Movie Review
    • Movie Review
  • More
    • Vue. Js
    • Php Question
    • Php Interview Question
    • Laravel Interview Question
    • SQL Interview Question
    • IAS Interview Question
    • PCS Interview Question
    • Technology
    • Other

09 March, 2024

Spring Security not working after API migration to Spring Boot 3

 Programing Coderfunda     March 09, 2024     No comments   

Software versions



* Spring Boot and dependencies: 3.2.2

* Spring Core : 6.1.3

* Spring Security 6.2.1

* Jetty Server: 11.0.20

* Languages: Java 17, Kotlin (Jetbrains Kotlin and Kotlin test library versions) 1.8.10






Below are modified code snippets for Jetty based Http Server Configuration, API Configuration and API Security Configuration which now uses a SecurityFilterChain instead of WebSecurityConfigurerAdapter.


Http Server Configuration
@Configuration
@EnableConfigurationProperties(ApiServiceProperties::class)
@ComponentScan("com......service")
@Import(value = [ApiSecurityConfig::class, WebFluxConfig::class])
class HttpServerConfig(var apiServiceProperties: ApiServiceProperties) {

/**
* Jetty Server Bean.
*/
@Bean
@SuppressWarnings("LongMethod")
fun jettyServer(
context: ApplicationContext,
springSecurityFilterChain: Filter,
mdcSetterFilter: MdcSetterFilter,
webContextFilter: WebContextFilter
): Server {
LOG.info(
"Starting Jetty server with " + ""
)

.. code removed ..

ServletContextHandler(server, "").apply {
val servlet = JettyHttpHandlerAdapter(WebHttpHandlerBuilder.applicationContext(context).build())
addServlet(ServletHolder(servlet), "/")

addFilter(FilterHolder(mdcSetterFilter), "/*", EnumSet.of(DispatcherType.REQUEST))
addFilter(FilterHolder(webContextFilter), "/*", EnumSet.of(DispatcherType.REQUEST))

// The ping endpoint should be unsecured, therefore ignored by the security filter
addFilter(
FilterHolder { request: ServletRequest, response: ServletResponse, chain: FilterChain ->
if (request is HttpServletRequest && request.requestURI != "/v1/ping") {
springSecurityFilterChain.doFilter(request, response, chain)
} else {
chain.doFilter(request, response)
}
},
"/v1/*",
EnumSet.of(DispatcherType.REQUEST)
)
}.start()

.. code removed ..

server.start()

LOG.info("Started Jetty server.")
return server
}

.. code removed ..
}



API Configuration
@Configuration
@ComponentScan(basePackages = [
"com......security",
"com......service"
])
@EnableConfigurationProperties(ApiServiceProperties::class)
@Import(HttpServerConfig::class)
class ApiServiceConfig : AbstractSpringBasedApplicationConfig()



API Security Configuration
@Configuration
@EnableWebSecurity
@ComponentScan("com......security", "com......service")
@EnableMethodSecurity(prePostEnabled = false, jsr250Enabled = true)
class ApiSecurityConfig(
private val restAuthenticationEntryPoint: RestAuthenticationEntryPoint,
private val restAuthenticationProvider: RestAuthenticationProvider
) {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.cors { }
.anonymous { it.disable() }
.httpBasic { it.disable() }
.formLogin { it.disable() }
.logout { it.disable() }
.csrf { it.disable() }
.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
.exceptionHandling { it.authenticationEntryPoint(restAuthenticationEntryPoint) }
.authenticationManager { authentication -> restAuthenticationProvider.authenticate(authentication) }
.addFilterBefore(RestAuthenticationTokenFilter(), AnonymousAuthenticationFilter::class.java)
.authorizeHttpRequests { it.requestMatchers("/**").permitAll().anyRequest().authenticated() }
return http.build()
}

@Bean
fun corsConfigurationSource(): CorsConfigurationSource = UrlBasedCorsConfigurationSource().apply {
registerCorsConfiguration(
"/**",
CorsConfiguration().applyPermitDefaultValues().apply {
allowedMethods = listOf("POST", "GET", "PUT", "DELETE", "HEAD")
}
)
}
}



Custom authentication provider
@Component
class RestAuthenticationProvider(
private val securityServiceClient: SecurityServiceClient,
private val cryptoService: CryptoService
) : AuthenticationProvider {

/**
* Given a [token] and [verifiedTokenModel], return a new User with granted authorities.
*/
private fun createAuthenticatedUser(token: String, verifiedTokenModel: VerifiedTokenModel) = User
.withUsername(verifiedTokenModel.verifiedPrincipalModel.id)
.password(token)
.authorities(verifiedTokenModel.verifiedPrincipalModel.scopes.map { scope ->
SimpleGrantedAuthority("ROLE_${scope.toUpperCase()}")
})
.build()

/**
* Given a [verifiedTokenModel], create a JSON Web Token to represent the authorizations of the verified principal.
*/
private fun createJwt(verifiedTokenModel: VerifiedTokenModel) = cryptoService.createAuthToken(
.. code removed ..
)

override fun authenticate(authentication: Authentication): Authentication? =
(authentication as? RestAuthenticationToken)?.token?.let { token ->
try {
val verifiedTokenModel = securityServiceClient.verifyToken(token)
val user = createAuthenticatedUser(token = token, verifiedTokenModel = verifiedTokenModel)

RestAuthenticationToken(
.. code removed ..
jwt = createJwt(verifiedTokenModel = verifiedTokenModel)
)
} catch (e: ReplyException) {
.. code removed ..
}
}

.. code removed ..
}



Below is a comparison of the new and old code for security configuration (Spring Boot 2.6.2 and Spring Core 5.3.14)





Postman request always receives a 403 response





Logs (without permit all)
DEBUG c.a.e.d.api.v1.security.MdcSetterFilter : Setting MDC logging context.
DEBUG c.a.e.d.a.v1.security.WebContextFilter : Setting WebContext on message
DEBUG o.s.security.web.FilterChainProxy : Securing GET /v1/clients/*/brands
INFO c.a.e.d.api.v1.config.HttpServerConfig : Token ::
DEBUG o.s.s.w.access.AccessDeniedHandlerImpl : Responding with 403 status code



I also tried passing it.requestMatchers("/**").permitAll().anyRequest().authenticated() in the call to authorizeHttpRequests() however that results in a different failure behavior


Logs (with permit all)
DEBUG c.a.e.d.api.v1.security.MdcSetterFilter : Setting MDC logging context.
DEBUG c.a.e.d.a.v1.security.WebContextFilter : Setting WebContext on message
DEBUG o.s.security.web.FilterChainProxy : Securing GET /v1/clients/*/brands
INFO c.a.e.d.api.v1.config.HttpServerConfig : Token ::
...
...
DEBUG o.s.w.s.adapter.HttpWebHandlerAdapter : [49377233] HTTP GET "/v1/clients/*/brands"
...
DEBUG s.w.r.r.m.a.RequestMappingHandlerMapping: [49377233] Mapped to com......service.ClientsApiController#listBrands(String, ServerHttpRequest)
DEBUG AuthorizationManagerBeforeMethodInterceptor: Authorizing method invocation ReflectiveMethodInvocation: public org.springframework.http.ResponseEntity com......service.ClientsApiController.listBrands(..); target is of class [com......service.ClientsApiController]

DEBUG AuthorizationManagerBeforeMethodInterceptor: Failed to authorize ReflectiveMethodInvocation: public org.springframework.http.ResponseEntity com......service.ClientsApiController.listBrands(...); target is of class [com......service.ClientsApiController] with authorization manager org.springframework.security.config.annotation.method.configuration.DeferringObservationAuthorizationManager@2323fe6a and decision AuthorityAuthorizationDecision [granted=false, authorities=[ROLE_READ_BRANDS]]
DEBUG s.w.r.r.m.a.RequestMappingHandlerAdapter: [49377233] Using @ExceptionHandler com......service.DefaultExceptionHandler#onThrowable(Throwable, ServerWebExchange)
DEBUG o.s.w.s.adapter.HttpWebHandlerAdapter : [49377233] Completed 403 FORBIDDEN




* Have tried multiple combinations of the security chain as suggested on several similar threads

* Added a few more log statements in security configuration code to capture these events and help understand how the new flow works

* DEBUG level log statements added inside the RestAuthenticationProvider.authenticate() are not showing up in the logs, indicating it is not getting invoked, and the flow is breaking before reaching that point.






However I suspect that the configured AuthenticationProvider (tried using authenticationProvider(..) earlier, but that did not work either) and AuthenticationManager are not getting plugged in the chain for some reason. Need help from the community in guiding me to set this up correctly. Thank you.
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg
Email ThisBlogThis!Share to XShare to Facebook
Newer Post Older Post Home

0 comments:

Post a Comment

Thanks

Meta

Popular Posts

  • Show page numbers as navigation in Laravel pagination
      Answer Sorted by:                                                Highest score (default)                                                  ...
  • How to monitor process status during process lifetime
    I need to track the process status ps axf during executable lifetime. Let's say I have executable main.exec and want to store into a fi...
  • Different ways of passing a variable by reference?
    I want to pass a variable to a function and have the function change the value. I know I can do this as an object but am wondering if the...
  • Use Flags For Countries & Languages in Laravel Blade Views
      Blade Flags   is a package to efficiently use   TwEmoji Countries & Languages Flags   in your Laravel Blade views. You can use it easi...
  • How to Run a Python File on a Specific Virtual Desktop Only?
    I want to run a Python script on a specific virtual desktop without affecting other desktops. Currently, when I execute my Python file us...

Categories

  • Ajax (26)
  • Bootstrap (30)
  • DBMS (42)
  • HTML (12)
  • HTML5 (45)
  • JavaScript (10)
  • Jquery (34)
  • Jquery UI (2)
  • JqueryUI (32)
  • Laravel (1017)
  • Laravel Tutorials (23)
  • Laravel-Question (6)
  • Magento (9)
  • Magento 2 (95)
  • MariaDB (1)
  • MySql Tutorial (2)
  • PHP-Interview-Questions (3)
  • Php Question (13)
  • Python (36)
  • RDBMS (13)
  • SQL Tutorial (79)
  • Vue.js Tutorial (69)
  • Wordpress (150)
  • Wordpress Theme (3)
  • codeigniter (108)
  • oops (4)
  • php (853)

Social Media Links

  • Follow on Twitter
  • Like on Facebook
  • Subscribe on Youtube
  • Follow on Instagram

Pages

  • Home
  • Contact Us
  • Privacy Policy
  • About us

Blog Archive

  • July (4)
  • September (100)
  • August (50)
  • July (56)
  • June (46)
  • May (59)
  • April (50)
  • March (60)
  • February (42)
  • January (53)
  • December (58)
  • November (61)
  • October (39)
  • September (36)
  • August (36)
  • July (34)
  • June (34)
  • May (36)
  • April (29)
  • March (82)
  • February (1)
  • January (8)
  • December (14)
  • November (41)
  • October (13)
  • September (5)
  • August (48)
  • July (9)
  • June (6)
  • May (119)
  • April (259)
  • March (122)
  • February (368)
  • January (33)
  • October (2)
  • July (11)
  • June (29)
  • May (25)
  • April (168)
  • March (93)
  • February (60)
  • January (28)
  • December (195)
  • November (24)
  • October (40)
  • September (55)
  • August (6)
  • July (48)
  • May (2)
  • January (2)
  • July (6)
  • June (6)
  • February (17)
  • January (69)
  • December (122)
  • November (56)
  • October (92)
  • September (76)
  • August (6)

Loading...

Laravel News

Loading...

Copyright © CoderFunda | Powered by Blogger
Design by Coderfunda | Blogger Theme by Coderfunda | Distributed By Coderfunda