在 Quarkus OIDC 的整合中,主要分成會分成 web-app 與 service 兩種 type。應用 OIDC 授權流程可以保護我們的 HTTP 端點,並且利用到 OIDC 的授權伺服器例如 Keycloak.
在 web-app 模式下,啟用 OIDC 的授權碼流程機制( Authorization Code Flow mechanism )時,使用者在未登入情況下,當遇到需要權限的頁面時,會遇到以下流程
如下圖所示
今天會作一個 /api/users/me 來取得目前的使用者,並整合 keycloak 授權碼流程機制。
%prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=backend-service
quarkus.oidc.credentials.secret=secret
# hybrid 表示 web-app, service 兩種通吃
quarkus.oidc.application-type=hybrid
# 所有 endpont 都要驗證 /*
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
# Tell Dev Services for Keycloak to import the realm file
quarkus.keycloak.devservices.realm-path=quarkus-realm.json
在 /src/main/kotlin/.../resource 建立 TokenResource.kt
import io.quarkus.oidc.IdToken
import org.eclipse.microprofile.jwt.JsonWebToken
import javax.inject.Inject
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
@Path("/api/tokens")
class TokenResource {
    @Inject
    @IdToken
    lateinit var idToken: JsonWebToken
    @Inject
    lateinit var accessToken: JsonWebToken
    @Produces("text/html")
    @GET
    fun getTokens() = "<html><body><h3>idToken</h3>$idToken" +
        "<h3>accessToken</h3>$accessToken</body></html>"
}
http://localhost:8080/api/tokens , 會被自動轉打到 keycloak ,並且後面有 redirect url

登入後可以看到印出 token 內容

在 /src/main/kotlin/.../resource 建立 UsersResource.kt 並輸入以下內容
package tw.brandy.ironman.resource
import io.quarkus.oidc.IdToken
import org.eclipse.microprofile.jwt.JsonWebToken
import org.jboss.resteasy.reactive.NoCache
import javax.inject.Inject
import javax.ws.rs.GET
import javax.ws.rs.Path
@Path("/api/users")
class UsersResource {
    @Inject
    lateinit var idToken: JsonWebToken
    @GET
    @Path("/me")
    @NoCache
    fun me(): User {
        return User(idToken)
    }
}
class User internal constructor(idToken: JsonWebToken) {
    val userName: String
    init {
        userName = idToken.name
    }
}
http://localhost:8080/api/users/me 會看到帶出目前使用者

properites 要加上三條以供 test
%test.quarkus.oidc.application-type=service
%test.quarkus.oidc.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB
smallrye.jwt.sign.key.location=/privateKey.pem
privateKey.pem 請從這裡取得 https://raw.githubusercontent.com/quarkusio/quarkus/main/integration-tests/oidc-tenancy/src/main/resources/privateKey.pem
import io.quarkus.test.junit.QuarkusTest
import io.restassured.module.kotlin.extensions.Given
import io.restassured.module.kotlin.extensions.Then
import io.restassured.module.kotlin.extensions.When
import io.smallrye.jwt.build.Jwt
import org.hamcrest.CoreMatchers.`is`
import org.junit.jupiter.api.Test
@QuarkusTest
class UserResourceTest {
    @Test
    fun `test api me`() {
        Given {
            auth().oauth2(getAccessToken("alice"))
        } When {
            get("/api/users/me")
        } Then {
            statusCode(200)
            body("userName", `is`("alice"))
        }
    }
    private fun getAccessToken(userName: String): String {
        return Jwt.preferredUserName(userName)
            .issuer("https://server.example.com")
            .audience("https://service.example.com")
            .sign()
    }
}
測試也跑過了!!收功?

發現其他測試失敗了,阿,因為都沒有驗證,明天來補補~
