我尝试用jwt实现authn/authz,如下所示:
class MainVerticle : CoroutineVerticle() {
private suspend fun initConfig(): JsonObject {
val yamlConfigOpts = ConfigStoreOptions()
.setFormat("yaml")
.setType("file")
.setConfig(JsonObject().put("path", "config.yaml"))
val configRetrieverOpts = ConfigRetrieverOptions()
.addStore(yamlConfigOpts)
val configRetriever = ConfigRetriever.create(vertx, configRetrieverOpts)
return configRetriever.config.await()
}
private suspend fun createJwtAuth(client: WebClient, config: JsonObject): JWTAuth? {
val issuer = config.getJsonObject("jwt").getString("issuer")
// derive JWKS uri from Keycloak issuer URI
val jwksUri = URI.create("${issuer}/protocol/openid-connect/certs")
// The exception will be caught above
val res = client.get(jwksUri.host, jwksUri.path).send().await()
return res.bodyAsJsonObject()?.let {
val keys = it.getJsonArray("keys")
val jwtOpt = JWTOptions()
jwtOpt.issuer = issuer
// configure JWTAuth
val jwtAuthOptions = JWTAuthOptions()
jwtAuthOptions.jwks = (keys.list as List<Map<String, *>>)
.map { json -> JsonObject(json) }
.map { json -> json.put("permissionsClaimKey", "realm_access/roles") }
jwtAuthOptions.jwtOptions = jwtOpt
JWTAuth.create(vertx, jwtAuthOptions)
} ?: throw AuthenticationException("Can not receive the token")
}
private fun createRoutes(router: Router, jwtAuth: JWTAuth): Unit {
router.route("/api/*").handler(JWTAuthHandler.create(jwtAuth))
router.route("/api/greet").handler {
val token = it.request().getHeader(HttpHeaders.AUTHORIZATION).substring("Bearer ".length)
jwtAuth.authenticate(JsonObject().put("jwt", token))
.onSuccess { user ->
val res = it.response()
res.putHeader("content-type", "text/plain")
// Write to the response and end it
res.end("I am interests path")
}
.onFailure { err -> it.response().setStatusCode(403).end(err.message) }
}
}
private suspend fun server(router: Router): HttpServer {
val server = vertx.createHttpServer()
return server.requestHandler(router)
.listen(8080)
.onSuccess {
println("HTTP server started on port ${it.actualPort()}")
}
.onFailure {
println("Failed to start the server. Reason ${it.message}")
}
.await()
}
override suspend fun start() {
val config = initConfig()
val webClient = WebClient.create(vertx)
val router = Router.router(vertx)
createJwtAuth(webClient, config)?.let {
createRoutes(router, it)
server(router)
}
}
}
在 /api/greet
路由处理程序,我想读出用户的声明,例如角色或名称。但不幸的是,我只有这些:
问题是,如何阅读用户的声明?
我使用keydepot作为身份提供者和vertx版本 4.0.0.CR1
.
更新
我已更改为oauth2 auth provider,并将代码调整如下:
class MainVerticle : CoroutineVerticle() {
private suspend fun createJwtAuth(): OAuth2Auth =
KeycloakAuth.discover(
vertx,
OAuth2Options()
.setFlow(OAuth2FlowType.AUTH_CODE)
.setClientID("svc")
.setClientSecret("secret")
.setSite("https://oic.example.io/auth/realms/vertx")
).await()
private fun createRoutes(router: Router, auth: OAuth2Auth): Unit {
val oauth2 = OAuth2AuthHandler.create(vertx, auth)
router.route("/api/*").handler(oauth2)
router.route("/api/greet").handler {
println(it.user().principal().getString("preferred_username"))
val res = it.response()
res.putHeader("content-type", "text/plain")
// Write to the response and end it
res.end("I am interests path")
}
}
private suspend fun server(router: Router): HttpServer {
val server = vertx.createHttpServer()
return server.requestHandler(router)
.listen(8080)
.onSuccess {
println("HTTP server started on port ${it.actualPort()}")
}
.onFailure {
println("Failed to start the server. Reason ${it.message}")
}
.await()
}
override suspend fun start() {
val router = Router.router(vertx)
createRoutes(router, createJwtAuth())
server(router)
}
}
线路
println(it.user().principal().getString("preferred_username"))
印刷品 null
而是用户名。我做错什么了?
1条答案
按热度按时间zysjyyx41#
当您使用最新版本时,让我解释一下新api中的一些变化。在4.0.0中我们分开了
authn
以及authz
所以在你的例子中你已经执行了authn
正确并获得User
对象示例。现在您想提取权限,就像您正在做的那样
JWTAuth
你走的是“低级”路线,如果你想用OAuth2Auth
有些事情是不必要的(比如装钥匙等等)。既然有了用户对象,就需要有
authz
提取对象。为此,我将举例说明java api,但在kotlin中,它应该非常类似:因此,权限是从
attributes
在claimKey
你把它交给提取器。简而言之,attributes
是由框架生成和/或解码/验证的数据,而principal
是为创建用户而提供的源数据。区别很重要,因为现在用户对象可以用于服务器验证和客户端请求。这也意味着保持源代码不变,解码不会覆盖重要数据(如过期日期等)的风险。