Skip to content

#16: add spring security DSL #219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions kofu/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ plugins {
id("java-library")
}

ext["spring-security.version"] = "5.2.0.BUILD-SNAPSHOT"
ext["spring-security-config.version"] = "5.2.0.BUILD-SNAPSHOT"

dependencies {
api("org.springframework.boot:spring-boot")
api("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

implementation(project(":autoconfigure-adapter"))
implementation(project(":security-adapter"))
implementation("org.jetbrains.kotlin:kotlin-reflect")

compileOnly("org.springframework:spring-webmvc")
compileOnly("org.springframework:spring-webflux")
compileOnly("org.springframework.boot:spring-boot-starter-security")
compileOnly("de.flapdoodle.embed:de.flapdoodle.embed.mongo")
compileOnly("org.springframework.data:spring-data-mongodb")
compileOnly("org.mongodb:mongodb-driver-reactivestreams")
Expand All @@ -37,6 +42,7 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-mustache")
testImplementation("org.springframework.boot:spring-boot-starter-json")
testImplementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive")
testImplementation("org.springframework.boot:spring-boot-starter-security")
testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin")
testRuntimeOnly("de.flapdoodle.embed:de.flapdoodle.embed.mongo")
testImplementation("io.mockk:mockk:1.9")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.fu.kofu.webflux

import org.springframework.context.support.GenericApplicationContext
import org.springframework.fu.kofu.AbstractDsl
import org.springframework.fu.kofu.ConfigurationDsl
import org.springframework.security.authentication.ReactiveAuthenticationManager
import org.springframework.security.config.annotation.web.reactive.SecurityInitializer
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService
import org.springframework.security.core.userdetails.ReactiveUserDetailsService
import org.springframework.security.crypto.password.PasswordEncoder

/**
* Kofu DSL for spring-security.
*
* Configure spring-security.
*
* Required dependencies can be retrieve using `org.springframework.boot:spring-boot-starter-security`.
*
* @author Jonas Bark
*/
class SecurityDsl(private val init: SecurityDsl.() -> Unit) : AbstractDsl() {

var authenticationManager: ReactiveAuthenticationManager? = null

var reactiveUserDetailsService: ReactiveUserDetailsService? = null

var passwordEncoder: PasswordEncoder? = null

var userDetailsPasswordService: ReactiveUserDetailsPasswordService? = null

internal var httpSecurity: ServerHttpSecurity? = null

override fun initialize(context: GenericApplicationContext) {
super.initialize(context)
init()
SecurityInitializer(
authenticationManager,
reactiveUserDetailsService,
passwordEncoder,
userDetailsPasswordService,
httpSecurity
).initialize(context)
}


class HttpSecurityDsl(
private val init: ServerHttpSecurity.() -> Unit,
private val securityDsl: SecurityDsl
) : AbstractDsl() {

private val httpSecurity: ServerHttpSecurity = ServerHttpSecurity.http()

override fun initialize(context: GenericApplicationContext) {
super.initialize(context)
httpSecurity.apply(init)
securityDsl.httpSecurity = httpSecurity
}

}

fun http(dsl: ServerHttpSecurity.() -> Unit = {}) {
HttpSecurityDsl(dsl, this).initialize(context)
}
}

/**
* Configure spring-security.
*
* Require `org.springframework.boot:spring-boot-starter-security` dependency.
*
* @sample org.springframework.fu.kofu.samples.securityDsl
* @author Jonas Bark
*/
fun ConfigurationDsl.security(dsl: SecurityDsl.() -> Unit = {}) {
SecurityDsl(dsl).initialize(context)
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.fu.kofu.webflux

import org.springframework.context.support.GenericApplicationContext
import org.springframework.fu.kofu.AbstractDsl
import org.springframework.security.authentication.ReactiveAuthenticationManager
import org.springframework.security.config.annotation.web.reactive.SecurityInitializer
import org.springframework.security.config.annotation.web.reactive.WebFluxSecurityInitializer
import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService
import org.springframework.security.core.userdetails.ReactiveUserDetailsService
import org.springframework.security.crypto.password.PasswordEncoder

/**
* Kofu DSL for spring-security.
*
* Configure spring-security.
*
* Required dependencies can be retrieve using `org.springframework.boot:spring-boot-starter-security`.
*
* @author Jonas Bark
*/
class WebFluxSecurityDsl(private val init: WebFluxSecurityDsl.() -> Unit) : AbstractDsl() {

override fun initialize(context: GenericApplicationContext) {
super.initialize(context)
init()
WebFluxSecurityInitializer().initialize(context)
}
}

/**
* Configure spring-security.
*
* Require `org.springframework.boot:spring-boot-starter-security` dependency.
*
* @sample org.springframework.fu.kofu.samples.securityDsl
* @author Jonas Bark
*/
fun WebFluxServerDsl.webFluxSecurity(dsl: WebFluxSecurityDsl.() -> Unit = {}) {
WebFluxSecurityDsl(dsl).initialize(context)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.springframework.fu.kofu.samples

import org.springframework.boot.WebApplicationType
import org.springframework.fu.kofu.application
import org.springframework.fu.kofu.webflux.security
import org.springframework.fu.kofu.webflux.webFlux
import org.springframework.fu.kofu.webflux.webFluxSecurity

fun securityDsl() {
application(WebApplicationType.REACTIVE) {
security()
webFlux {
webFluxSecurity()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.fu.kofu.webflux

import org.junit.jupiter.api.Test
import org.springframework.boot.WebApplicationType
import org.springframework.fu.kofu.application
import org.springframework.fu.kofu.localServerPort
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService
import org.springframework.security.core.userdetails.User
import org.springframework.test.web.reactive.server.WebTestClient
import java.nio.charset.Charset
import java.util.*


/**
* @author Jonas Bark
*/
class SecurityDslTests {

@Test
fun `Check spring-security configuration DSL`() {

val username = "user"
val password = "password"
val repoAuthenticationManager =
UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService(username, password))

val app = application(WebApplicationType.REACTIVE) {
security {
authenticationManager = repoAuthenticationManager
http {
authenticationManager(repoAuthenticationManager)
headers()
logout()
}
}
webFlux {
port = 0
webFluxSecurity()
router {
GET("/view") { ok().build() }
}
}
}
with(app.run()) {
val client = WebTestClient.bindToServer().baseUrl("http://127.0.0.1:$localServerPort").build()
client.get().uri("/view").exchange()
.expectStatus().isUnauthorized

val basicAuth =
Base64.getEncoder().encode("$username:$password".toByteArray())?.toString(Charset.defaultCharset())
client.get().uri("/view").header("Authorization", "Basic $basicAuth").exchange()
.expectStatus().is2xxSuccessful

close()
}
}

private fun userDetailsService(username: String, password: String): MapReactiveUserDetailsService {
@Suppress("DEPRECATION")
val user = User.withDefaultPasswordEncoder()
.username(username)
.password(password)
.roles("USER")
.build()
return MapReactiveUserDetailsService(user)
}

}
14 changes: 14 additions & 0 deletions security-adapter/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
= Spring Boot auto-configuration adapter

This Java library adapts JavaConfig based `spring-boot-autoconfigure` to functional
configuration based on function bean registration which is known to be faster and
consumes less memory.

It provides a collection of `ApplicationContextInitializer` that
leverage Spring Boot auto-configurations to register the same beans but in a functional
way. For now, it mostly targets the Reactive stack and conditions are not managed yet.

Ideally in the future, such library should be generated during Spring Boot build based
on `spring-boot-autoconfigure` classes.

The dependency to use is `org.springframework.fu:spring-fu-security-adapter`.
38 changes: 38 additions & 0 deletions security-adapter/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
id("io.spring.dependency-management")
id("java-library")
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

ext["spring-security.version"] = "5.2.0.BUILD-SNAPSHOT"
ext["spring-security-config.version"] = "5.2.0.BUILD-SNAPSHOT"

dependencies {
api("org.springframework.boot:spring-boot")
api("org.springframework.boot:spring-boot-autoconfigure")

compileOnly("org.springframework:spring-webflux")
compileOnly("org.springframework.boot:spring-boot-starter-security")
}

repositories {
maven("https://repo.spring.io/milestone")
}

publishing {
publications {
create(project.name, MavenPublication::class.java) {
from(components["java"])
artifactId = "spring-fu-security-adapter"
val sourcesJar by tasks.creating(Jar::class) {
classifier = "sources"
from(sourceSets["main"].allSource)
}
artifact(sourcesJar)
}
}
}
Loading