Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin OAuth在Ktor中使用Keyclope:它应该像这样工作吗?_Kotlin_Authentication_Oauth 2.0_Keycloak_Ktor - Fatal编程技术网

Kotlin OAuth在Ktor中使用Keyclope:它应该像这样工作吗?

Kotlin OAuth在Ktor中使用Keyclope:它应该像这样工作吗?,kotlin,authentication,oauth-2.0,keycloak,ktor,Kotlin,Authentication,Oauth 2.0,Keycloak,Ktor,我试图在Ktor web服务器中通过KeyClope设置一个工作的Oauth2授权。预期的流程是从web服务器向Key斗篷发送请求并登录到给定的UI,然后Key斗篷发回可用于接收令牌的代码。像 首先,我是根据Ktor文档中的示例来做的。它工作得很好,直到我必须接收令牌,然后它只给我HTTP状态401。即使curl命令工作正常。然后我尝试了一个我在上找到的示例项目,我通过构建自己的HTTP请求并将其发送到KeyClope服务器以接收令牌来实现它,但它应该是这样工作的吗 关于这一点,我有很多问题 这

我试图在Ktor web服务器中通过KeyClope设置一个工作的Oauth2授权。预期的流程是从web服务器向Key斗篷发送请求并登录到给定的UI,然后Key斗篷发回可用于接收令牌的代码。像

首先,我是根据Ktor文档中的示例来做的。它工作得很好,直到我必须接收令牌,然后它只给我HTTP状态401。即使curl命令工作正常。然后我尝试了一个我在上找到的示例项目,我通过构建自己的HTTP请求并将其发送到KeyClope服务器以接收令牌来实现它,但它应该是这样工作的吗

关于这一点,我有很多问题

  • 这个函数应该同时处理授权和获取令牌吗

     authenticate(keycloakOAuth) {
         get("/oauth") {
             val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
    
             call.respondText("Access Token = ${principal?.accessToken}")
         }
     }
    
  • 我用一个内置的HTTP请求创建了一个/token路由,这个路由成功地获得了token,但感觉像是黑客攻击

    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()}")
    }
    
    get(“/token”){
    var grantType=“授权\代码”
    val code=call.request.queryParameters[“code”]
    val requestBody=“grant_type=${grantType}&”+
    “客户端\u id=${keydeposeprovider.clientId}&”+
    “客户端_secret=${keydeposeprovider.clientSecret}&”+
    “code=${code.toString()}&”+
    "重定向"uri=http://localhost:8080/token"
    val tokenResponse=httpClient.post(keydeposeprovider.accessTokenUrl){
    标题{
    追加(“内容类型”、“应用程序/x-www-form-urlencoded”)
    }
    body=requestBody
    }
    call.respondText(“访问令牌=${tokenResponse.readText()}”)
    }
    

  • TL;DR:我可以通过KeyClope登录,但尝试获取访问令牌会给我401。ktor中的authenticate函数也应该处理这个问题吗?

    第一个问题的答案是:如果该路由对应于在
    urlProvider
    lambda中返回的重定向URI,那么它将同时用于这两个问题

    整个过程如下:

  • 用户打开http://localhost:7777/login (浏览器中
    认证下的任何路由)
  • Ktor重定向到
    authorizeUrl
    传递必要的参数
  • 用户通过KeyClope UI登录
  • KeyClope将用户重定向到由
    urlProvider
    lambda提供的重定向URI,并传递获取访问令牌所需的参数
  • Ktor向令牌URL发出请求,并执行与重定向URI对应的路由处理程序(http://localhost:7777/callback 在示例中)
  • 在处理程序中,您可以访问
    OAuthAccessTokenResponse
    对象,该对象具有访问令牌、刷新令牌和从KeyClope返回的任何其他参数的属性
  • 以下是工作示例的代码:

    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)
    }
    
    val provider=OAuthServerSettings.OAuth2ServerSettings(
    name=“keydeave”,
    授权URL=”http://localhost:8080/auth/realms/master/protocol/openid-连接/验证“,
    accessTokenUrl=”http://localhost:8080/auth/realms/$realm/protocol/openid connect/token“,
    clientId=clientId,
    clientSecret=clientSecret,
    requestMethod=HttpMethod.Post//此提供程序不支持GET-HTTP方法
    )
    主要内容(){
    嵌入式服务器(网络,端口=7777){
    安装(身份验证){
    oauth(“keydove_oauth”){
    client=HttpClient(Apache)
    providerLookup={provider}
    //URL应与KeyClope客户端设置中的“有效重定向URI”模式匹配
    urlProvider={”http://localhost:7777/callback" }
    }
    }
    路由{
    验证(“keydove_oauth”){
    获取(“登录”){
    //用户将首先被重定向到authorizeUrl
    }
    路由(“/callback”){
    //此处理程序将在向提供者的令牌URL发出请求后执行。
    处理{
    val principal=call.authentication.principal()
    if(主体!=null){
    val response=主体作为OAuthAccessTokenResponse.OAuth2
    call.respondText{“访问令牌:${response.accessToken}”}
    }否则{
    call.respondText{“无主体”}
    }
    }
    }
    }
    }
    }.start(等待=false)
    }
    
    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)
    }