領先一步
VMware 提供培訓和認證,以加速您的進度。
了解更多嗨,Spring 粉絲們!歡迎收看新一集的 Spring Tips。在本集中,我們將研究 Spring Security 的全新 Kotlin DSL。我熱愛 Kotlin。我曾在其他幾個 Spring Tips 影片中介紹過 Kotlin:Kotlin 程式語言、Bootiful Kotlin Redux,以及 Spring 對 Coroutines 的支援。其中一些影片非常舊!Spring 生態系統中已經有許多不同的專案正在發布 Kotlin DSL。其中包括 Spring Framework、Spring Webflux、Spring Data、Spring Cloud Contract 和 Spring Cloud Gateway 等。現在,還有 Spring Security!
Spring Security 是一個很棒的專案 - 它解決了業界一些最棘手的問題,並幫助人們保護他們的應用程式。而且,似乎這還不夠,它還堅定地決心讓安全性變得簡單。如果您曾經在其最早的版本中使用過 Spring Security,您就會知道它需要大量的 XML - 頁面! - 才能完成任何事情。後來有所改進,在 Spring Security 3 中,您可以使用一兩個 XML 節點以常識預設值鎖定應用程式。然後,在 Spring Security 4 中,他們引入了一個 Java DSL,讓人們可以使用他們的編譯器來驗證事物。漸漸地,隨著時間的推移,Spring Security 也引入了常識預設值。現在,您可以註冊一個 UserDetailsService
,Spring Security 將鎖定基於 Servlet 的 HTTP 應用程式中的所有 HTTP 端點,並要求進行身份驗證。再簡單不過了!或者可以更簡單嗎?在 Spring Security 5.2 中,他們對 Spring Security 進行了一些備受讚賞的改進。現在,除了使用過去流暢的 Java 配置 DSL 之外,還有一種新方法,您可以提供 lambda 表達式,並獲得一個可以使用的上下文物件。您不再需要縮排您的 Spring Security API,才能理解其意圖!現在,在本期中,我們將透過快速瀏覽全新的 Spring Security Kotlin DSL,將事情提升到一個新的水平。
請記住,Spring Security 解決了兩個正交問題:身份驗證和授權。身份驗證回答了問題:「誰正在提出請求?」是 Josh 還是 Jane?授權回答了問題:「請求者進入系統後擁有什麼權限?」身份驗證是關於插入身份提供者。有數百萬種方法可以做到這一點(Active Directory、記憶體中的使用者名稱和密碼、LDAP、SAML 等)。更多的是關於插入實作。我們將僅使用記憶體中的使用者名稱和密碼驗證管理器,因為我們需要它,而且這並非 DSL 真正擅長的地方。
DSL 在交換給定類型的實作時,以及在描述規則或自訂行為時最有用。因此,我們將使用 Spring Security DSL 自訂授權行為。
以下是一個基於 Spring Boot 的應用程式,它使用 Spring Security。我從 Spring Initializr 產生了一個新專案,它使用 Kotlin,並使用 Spring Security 和 Spring Boot 2.3.M2 或更高版本。它使用 functional bean registration DSL 以程式方式註冊 bean。我們在 2017 年的 Spring Tips 影片中討論了程式化的 bean 註冊。誠然,該影片展示了它在 Java 中的使用,但該應用程式在 Kotlin 中基本上相同:您可以透過將 bean 包裝在對 bean
函數的呼叫中來註冊 bean。
第一個 bean 是 InMemoryUserDetailsManager
。第二個 bean 是一個 functional HTTP 端點,/greetings
。當一個 HTTP 請求進入時,我們從當前請求中提取經過身份驗證的主體,提取名稱,然後建立一個 ServerResponse
,其主體將由一個 Map<String,String>
表示。
有趣的部分是類別 KotlinSecurityConfiguration
。它繼承了 WebSecurityConfigurerAdapter
。我們可能會在那裡覆寫幾個方法,但我選擇覆寫 configure
方法來指定兩件事:我想要選擇加入 HTTP BASIC 身份驗證,以及指定哪些路由需要身份驗證,哪些路由是完全開放的。下面的配置規定所有對 /greetings/**
(/greetings/
端點,以及它下面的任何內容,例如 /greetings/foo
)的請求都必須經過身份驗證。第二個規則表示其他所有內容都是完全開放的。非常重要的是,更具體的規則 - /greetings/**
- 必須位於更開放的規則之前。這些規則按順序從上到下進行評估。如果我們先放置第二個規則,那麼它將匹配每個請求,我們永遠不需要評估 /greetings
的規則 - 它實際上將保持完全開放!
package com.example.kotlinsecurity
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.support.beans
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.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.web.servlet.invoke
import org.springframework.security.core.userdetails.User
import org.springframework.security.provisioning.InMemoryUserDetailsManager
import org.springframework.web.servlet.function.ServerResponse
import org.springframework.web.servlet.function.router
@SpringBootApplication
class KotlinSecurityApplication
@EnableWebSecurity
class KotlinSecurityConfiguration : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity?) {
http {
httpBasic {}
authorizeRequests {
authorize("/greetings/**", hasAuthority("ROLE_ADMIN"))
authorize("/**", permitAll)
}
}
}
}
fun main(args: Array<String>) {
runApplication<KotlinSecurityApplication>(*args) {
addInitializers(beans {
bean {
fun user(user: String, pw: String, vararg roles: String) =
User.withDefaultPasswordEncoder().username(user).password(pw).roles(*roles).build()
InMemoryUserDetailsManager(user("jlong", "pw", "USER"), user("rwinch", "pw1", "USER", "ADMIN"))
}
bean {
router {
GET("/greetings") { request ->
request.principal().map { it.name }.map { ServerResponse.ok().body(mapOf("greeting" to "Hello, $it")) }.orElseGet { ServerResponse.badRequest().build() }
}
}
}
})
}
}
在本集中,我們介紹了 Spring Security 的全新 Kotlin DSL。文字比程式碼多,因為,這很重要,Spring Security 為您做了很多事情,因此 API 的表面積實際上是您想要在已經合理的預設值之外進行的自訂的最小範圍。我希望您學到了一些新東西,並將嘗試使用 Spring Security DSL。