如何获取当前用户的角色?
How to get the role from the current user?
我正在尝试使用 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
路由处理程序中,我想读出用户的声明,例如角色或名称。但不幸的是,这就是我所拥有的:
问题是,如何读取用户的声明?
我使用 Keycloak 作为身份提供者和 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
而不是用户名。我做错了什么?
由于您使用的是当前最新版本,让我稍微解释一下新 API 中的变化。在 4.0.0 中,我们拆分了 authn
和 authz
,因此在您的示例中,您已经正确地执行了 authn
并获得了一个 User
对象实例。
现在你想提取权限,因为你正在做 JWTAuth
你正在走“低”级路径,而如果你要使用 OAuth2Auth
有些事情就不会必要的(例如加载密钥等...)。
现在您有了一个用户对象,您还需要一个 authz
提取对象。为此,我将以 java API 为例,但在 Kotlin 中它应该非常相似:
// First create a JWTAuthorization object
JWTAuthorization.create("realm_access/roles")
.getAuthorizations(user)
.onSuccess(success -> {
// The authorizations have been successfully extracted from the user
// Now you can perform any kind of checks
if (PermissionBasedAuthorization.create("write").match(user)) {
// ... User is allowed to write...
}
});
因此,权限是从您传递给提取器的 claimKey
下的 attributes
中提取的。简而言之,attributes
是由框架生成的和/或 decoded/validated 数据,而 principal
是为创建用户而提供的源数据。区别很重要,因为现在用户对象可用于服务器验证和客户端请求。
这也意味着保持源不变,不存在解码会覆盖重要数据(如到期日期等)的风险。
我正在尝试使用 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
路由处理程序中,我想读出用户的声明,例如角色或名称。但不幸的是,这就是我所拥有的:
问题是,如何读取用户的声明?
我使用 Keycloak 作为身份提供者和 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
而不是用户名。我做错了什么?
由于您使用的是当前最新版本,让我稍微解释一下新 API 中的变化。在 4.0.0 中,我们拆分了 authn
和 authz
,因此在您的示例中,您已经正确地执行了 authn
并获得了一个 User
对象实例。
现在你想提取权限,因为你正在做 JWTAuth
你正在走“低”级路径,而如果你要使用 OAuth2Auth
有些事情就不会必要的(例如加载密钥等...)。
现在您有了一个用户对象,您还需要一个 authz
提取对象。为此,我将以 java API 为例,但在 Kotlin 中它应该非常相似:
// First create a JWTAuthorization object
JWTAuthorization.create("realm_access/roles")
.getAuthorizations(user)
.onSuccess(success -> {
// The authorizations have been successfully extracted from the user
// Now you can perform any kind of checks
if (PermissionBasedAuthorization.create("write").match(user)) {
// ... User is allowed to write...
}
});
因此,权限是从您传递给提取器的 claimKey
下的 attributes
中提取的。简而言之,attributes
是由框架生成的和/或 decoded/validated 数据,而 principal
是为创建用户而提供的源数据。区别很重要,因为现在用户对象可用于服务器验证和客户端请求。
这也意味着保持源不变,不存在解码会覆盖重要数据(如到期日期等)的风险。