Spring 技巧:Kotlin 和 Spring Security

工程 | Josh Long | 2020年3月4日 | ...

大家好,Spring 爱好者们! 欢迎收看另一期Spring 技巧。 在本集中,我们将介绍 Spring Security 的新 Kotlin DSL。 我喜欢 Kotlin。 我在其他几个 Spring 技巧视频中介绍了 Kotlin:Kotlin 编程语言Bootiful Kotlin ReduxSpring 对 Kotlin 协程的支持。 这些视频有些非常老了! 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 或更高版本。 它使用函数式 bean 注册 DSL 以编程方式注册 bean。 我们在2017 年的 Spring 技巧视频中讨论了编程 bean 注册。 当然,该视频演示了它在 Java 中的使用,但该应用程序在 Kotlin 中基本上是相同的:您可以通过将其包装在对 bean 函数的调用中来注册一个 bean。

第一个 bean 是 InMemoryUserDetailsManager。 第二个 bean 是一个函数式 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。

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看所有