Ktor 中带有 KeyCloak 的 OAuth:它应该像这样工作吗?
OAuth with KeyCloak in Ktor : Is it supposed to work like this?
我试图在 Ktor Web 服务器中通过 Keycloak 设置有效的 Oauth2 授权。预期的流程是从 Web 服务器向 keycloak 发送请求并登录给定的 UI,然后 Keycloak 发回可用于接收令牌的代码。喜欢here
首先我是根据Ktor文档中的例子做的。 Oauth It worked fine until it got to the point where I had to receive the token, then it just gave me HTTP status 401. Even though the curl command works properly. Then I tried an example project I found on GitHub ,我设法通过构建自己的 HTTP 请求并将其发送到 Keycloak 服务器以接收令牌来使其工作,但它应该像这样工作吗?
关于这个我有很多问题。
这个函数应该同时处理授权和获取令牌吗?
authenticate(keycloakOAuth) {
get("/oauth") {
val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
call.respondText("Access Token = ${principal?.accessToken}")
}
}
我认为我的配置是正确的,因为我可以获得授权,只是没有令牌。
const val KEYCLOAK_ADDRESS = "**"
val keycloakProvider = OAuthServerSettings.OAuth2ServerSettings(
name = "keycloak",
authorizeUrl = "$KEYCLOAK_ADDRESS/auth/realms/production/protocol/openid-connect/auth",
accessTokenUrl = "$KEYCLOAK_ADDRESS/auth/realms/production/protocol/openid-connect/token",
clientId = "**",
clientSecret = "**",
accessTokenRequiresBasicAuth = false,
requestMethod = HttpMethod.Post, // must POST to token endpoint
defaultScopes = listOf("roles")
)
const val keycloakOAuth = "keycloakOAuth"
install(Authentication) {
oauth(keycloakOAuth) {
client = HttpClient(Apache)
providerLookup = { keycloakProvider }
urlProvider = { "http://localhost:8080/token" }
}
}
我用内置的 HTTP 请求创建了这个 /token 路由,这个路由设法获得了令牌,但感觉像是 hack。
get("/token"){
var grantType = "authorization_code"
val code = call.request.queryParameters["code"]
val requestBody = "grant_type=${grantType}&" +
"client_id=${keycloakProvider.clientId}&" +
"client_secret=${keycloakProvider.clientSecret}&" +
"code=${code.toString()}&" +
"redirect_uri=http://localhost:8080/token"
val tokenResponse = httpClient.post<HttpResponse>(keycloakProvider.accessTokenUrl) {
headers {
append("Content-Type","application/x-www-form-urlencoded")
}
body = requestBody
}
call.respondText("Access Token = ${tokenResponse.readText()}")
}
TL;DR:我可以通过 Keycloak 正常登录,但尝试获取 access_token 时返回 401。ktor 中的身份验证功能是否也应该处理该问题?
第一个问题的答案:如果该路由与 urlProvider
lambda 中返回的重定向 URI 相对应,它将同时用于这两个问题。
整体流程如下:
- 用户在浏览器中打开 http://localhost:7777/login(
authenticate
下的任意路径)
- Ktor 重定向到
authorizeUrl
并传递必要的参数
- 用户通过Keycloak登录UI
- Keycloak 将用户重定向到
urlProvider
lambda 提供的重定向 URI,传递获取访问令牌所需的参数
- Ktor向token发出请求URL并执行重定向URI对应的路由处理器(示例中为http://localhost:7777/callback)
- 在处理程序中,您可以访问
OAuthAccessTokenResponse
对象,该对象具有访问令牌、刷新令牌和从 Keycloak 返回的任何其他参数的属性。
这是工作示例的代码:
val provider = OAuthServerSettings.OAuth2ServerSettings(
name = "keycloak",
authorizeUrl = "http://localhost:8080/auth/realms/master/protocol/openid-connect/auth",
accessTokenUrl = "http://localhost:8080/auth/realms/$realm/protocol/openid-connect/token",
clientId = clientId,
clientSecret = clientSecret,
requestMethod = HttpMethod.Post // The GET HTTP method is not supported for this provider
)
fun main() {
embeddedServer(Netty, port = 7777) {
install(Authentication) {
oauth("keycloak_oauth") {
client = HttpClient(Apache)
providerLookup = { provider }
// The URL should match "Valid Redirect URIs" pattern in Keycloak client settings
urlProvider = { "http://localhost:7777/callback" }
}
}
routing {
authenticate("keycloak_oauth") {
get("login") {
// The user will be redirected to authorizeUrl first
}
route("/callback") {
// This handler will be executed after making a request to a provider's token URL.
handle {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal != null) {
val response = principal as OAuthAccessTokenResponse.OAuth2
call.respondText { "Access token: ${response.accessToken}" }
} else {
call.respondText { "NO principal" }
}
}
}
}
}
}.start(wait = false)
}
我试图在 Ktor Web 服务器中通过 Keycloak 设置有效的 Oauth2 授权。预期的流程是从 Web 服务器向 keycloak 发送请求并登录给定的 UI,然后 Keycloak 发回可用于接收令牌的代码。喜欢here
首先我是根据Ktor文档中的例子做的。 Oauth It worked fine until it got to the point where I had to receive the token, then it just gave me HTTP status 401. Even though the curl command works properly. Then I tried an example project I found on GitHub ,我设法通过构建自己的 HTTP 请求并将其发送到 Keycloak 服务器以接收令牌来使其工作,但它应该像这样工作吗?
关于这个我有很多问题。
这个函数应该同时处理授权和获取令牌吗?
authenticate(keycloakOAuth) { get("/oauth") { val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>() call.respondText("Access Token = ${principal?.accessToken}") } }
我认为我的配置是正确的,因为我可以获得授权,只是没有令牌。
const val KEYCLOAK_ADDRESS = "**" val keycloakProvider = OAuthServerSettings.OAuth2ServerSettings( name = "keycloak", authorizeUrl = "$KEYCLOAK_ADDRESS/auth/realms/production/protocol/openid-connect/auth", accessTokenUrl = "$KEYCLOAK_ADDRESS/auth/realms/production/protocol/openid-connect/token", clientId = "**", clientSecret = "**", accessTokenRequiresBasicAuth = false, requestMethod = HttpMethod.Post, // must POST to token endpoint defaultScopes = listOf("roles") ) const val keycloakOAuth = "keycloakOAuth" install(Authentication) { oauth(keycloakOAuth) { client = HttpClient(Apache) providerLookup = { keycloakProvider } urlProvider = { "http://localhost:8080/token" } } }
我用内置的 HTTP 请求创建了这个 /token 路由,这个路由设法获得了令牌,但感觉像是 hack。
get("/token"){ var grantType = "authorization_code" val code = call.request.queryParameters["code"] val requestBody = "grant_type=${grantType}&" + "client_id=${keycloakProvider.clientId}&" + "client_secret=${keycloakProvider.clientSecret}&" + "code=${code.toString()}&" + "redirect_uri=http://localhost:8080/token" val tokenResponse = httpClient.post<HttpResponse>(keycloakProvider.accessTokenUrl) { headers { append("Content-Type","application/x-www-form-urlencoded") } body = requestBody } call.respondText("Access Token = ${tokenResponse.readText()}") }
TL;DR:我可以通过 Keycloak 正常登录,但尝试获取 access_token 时返回 401。ktor 中的身份验证功能是否也应该处理该问题?
第一个问题的答案:如果该路由与 urlProvider
lambda 中返回的重定向 URI 相对应,它将同时用于这两个问题。
整体流程如下:
- 用户在浏览器中打开 http://localhost:7777/login(
authenticate
下的任意路径) - Ktor 重定向到
authorizeUrl
并传递必要的参数 - 用户通过Keycloak登录UI
- Keycloak 将用户重定向到
urlProvider
lambda 提供的重定向 URI,传递获取访问令牌所需的参数 - Ktor向token发出请求URL并执行重定向URI对应的路由处理器(示例中为http://localhost:7777/callback)
- 在处理程序中,您可以访问
OAuthAccessTokenResponse
对象,该对象具有访问令牌、刷新令牌和从 Keycloak 返回的任何其他参数的属性。
这是工作示例的代码:
val provider = OAuthServerSettings.OAuth2ServerSettings(
name = "keycloak",
authorizeUrl = "http://localhost:8080/auth/realms/master/protocol/openid-connect/auth",
accessTokenUrl = "http://localhost:8080/auth/realms/$realm/protocol/openid-connect/token",
clientId = clientId,
clientSecret = clientSecret,
requestMethod = HttpMethod.Post // The GET HTTP method is not supported for this provider
)
fun main() {
embeddedServer(Netty, port = 7777) {
install(Authentication) {
oauth("keycloak_oauth") {
client = HttpClient(Apache)
providerLookup = { provider }
// The URL should match "Valid Redirect URIs" pattern in Keycloak client settings
urlProvider = { "http://localhost:7777/callback" }
}
}
routing {
authenticate("keycloak_oauth") {
get("login") {
// The user will be redirected to authorizeUrl first
}
route("/callback") {
// This handler will be executed after making a request to a provider's token URL.
handle {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal != null) {
val response = principal as OAuthAccessTokenResponse.OAuth2
call.respondText { "Access token: ${response.accessToken}" }
} else {
call.respondText { "NO principal" }
}
}
}
}
}
}.start(wait = false)
}