From 64c987f6c02e612da4357f3b9e59db77c06f1849 Mon Sep 17 00:00:00 2001 From: Raymond Yang Date: Tue, 24 May 2022 14:31:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E7=99=BB=E5=87=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/com/ray650128/Application.kt | 1 - src/main/kotlin/com/ray650128/model/User.kt | 4 +- .../kotlin/com/ray650128/plugins/Routing.kt | 105 ++++++++++++++++-- .../ray650128/repository/UserRepository.kt | 14 ++- .../com/ray650128/service/UserService.kt | 14 ++- .../kotlin/com/ray650128/utils/JwtConfig.kt | 36 ++++++ src/main/resources/application.conf | 4 +- 7 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/com/ray650128/utils/JwtConfig.kt diff --git a/src/main/kotlin/com/ray650128/Application.kt b/src/main/kotlin/com/ray650128/Application.kt index dc50a6b..895e400 100644 --- a/src/main/kotlin/com/ray650128/Application.kt +++ b/src/main/kotlin/com/ray650128/Application.kt @@ -12,5 +12,4 @@ fun Application.module() { DatabaseFactory.init() DatabaseFactory.createTable() configureRouting() - configureSecurity() } diff --git a/src/main/kotlin/com/ray650128/model/User.kt b/src/main/kotlin/com/ray650128/model/User.kt index e6da929..d13c51b 100644 --- a/src/main/kotlin/com/ray650128/model/User.kt +++ b/src/main/kotlin/com/ray650128/model/User.kt @@ -1,5 +1,7 @@ package com.ray650128.model +import io.ktor.server.auth.* + data class User( var id: Long? = -1, var name: String? = "", @@ -8,4 +10,4 @@ data class User( var token: String? = "", var createTime: Long? = -1, var updateTime: Long? = -1 -) +): Principal diff --git a/src/main/kotlin/com/ray650128/plugins/Routing.kt b/src/main/kotlin/com/ray650128/plugins/Routing.kt index 744e14a..480cc16 100644 --- a/src/main/kotlin/com/ray650128/plugins/Routing.kt +++ b/src/main/kotlin/com/ray650128/plugins/Routing.kt @@ -2,19 +2,34 @@ package com.ray650128.plugins import com.ray650128.model.LoginBody import com.ray650128.model.User -import com.ray650128.repository.UserRepository -import io.ktor.server.routing.* +import com.ray650128.service.UserService +import com.ray650128.utils.JwtConfig import io.ktor.http.* import io.ktor.serialization.gson.* import io.ktor.server.application.* +import io.ktor.server.auth.* +import io.ktor.server.auth.jwt.* import io.ktor.server.plugins.contentnegotiation.* -import io.ktor.server.response.* import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* fun Application.configureRouting() { - val userRepository = UserRepository() - + install(Authentication) { + jwt { + verifier(JwtConfig.verifier) + realm = JwtConfig.myRealm + validate { + val name = it.payload.getClaim("account").asString() + if (name != null) { + User(account = name, password = "") + } else { + null + } + } + } + } install(ContentNegotiation) { gson { } @@ -26,16 +41,86 @@ fun Application.configureRouting() { } post("/api/v1/register") { + val body = call.receive() + val token = JwtConfig.generateToken(body) + val data = User( + account = body.account, + password = body.password, + token = token + ) + val result = UserService().addUser(data) + if (result) { + call.respond( + status = HttpStatusCode.OK, + message = mapOf("token" to token) + ) + } else { + call.respond( + status = HttpStatusCode.BadRequest, + message = mapOf("message" to "User has exist.") + ) + } + } + post("/api/v1/login") { val body = call.receive() val data = User( account = body.account, password = body.password ) - userRepository.add(data) - call.respond( - status = HttpStatusCode.OK, - message = mapOf("token" to "aaaaaaa") - ) + // Check username and password + val user = UserService().getUser(data) + if (user != null) { + var token = user.token + if (token.isNullOrEmpty()) { + token = JwtConfig.generateToken(body) + user.token = token + UserService().updateUser(user) + } + call.respond( + status = HttpStatusCode.OK, + message = mapOf("token" to token) + ) + } else { + call.respond( + status = HttpStatusCode.Unauthorized, + message = mapOf("message" to "Account or Password wrong.") + ) + } + } + + authenticate { + post("/api/v1/updateUser") { + call.respond( + status = HttpStatusCode.OK, + message = mapOf("message" to "Test.") + ) + } + + post("/api/v1/logout") { + val account = call.authentication.principal()?.account + println("[Logout] Account: $account") + if (account.isNullOrEmpty()) { + call.respond( + status = HttpStatusCode.Unauthorized, + message = mapOf("message" to "Unauthorized.") + ) + } else { + val user = UserService().getUserByAccount(account) + if (user != null) { + user.token = null + UserService().updateUser(user) + call.respond( + status = HttpStatusCode.OK, + message = mapOf("message" to "success.") + ) + } else { + call.respond( + status = HttpStatusCode.Unauthorized, + message = mapOf("message" to "Unauthorized.") + ) + } + } + } } } } diff --git a/src/main/kotlin/com/ray650128/repository/UserRepository.kt b/src/main/kotlin/com/ray650128/repository/UserRepository.kt index 26931d0..f6cfc07 100644 --- a/src/main/kotlin/com/ray650128/repository/UserRepository.kt +++ b/src/main/kotlin/com/ray650128/repository/UserRepository.kt @@ -13,6 +13,7 @@ class UserRepository { Users.insert { it[account] = data.account it[password] = data.password + it[token] = data.token it[createTime] = System.currentTimeMillis() it[updateTime] = System.currentTimeMillis() } @@ -40,7 +41,18 @@ class UserRepository { return data } - suspend fun get(userAccount: String): User? { + suspend fun get(data: User): User? { + return transaction { + Users.select { + Users.account eq data.account + Users.password eq data.password + }.mapNotNull { + toUser(it) + }.singleOrNull() + } + } + + suspend fun getByAccount(userAccount: String): User? { return transaction { Users.select { Users.account eq userAccount }.mapNotNull { toUser(it) diff --git a/src/main/kotlin/com/ray650128/service/UserService.kt b/src/main/kotlin/com/ray650128/service/UserService.kt index 6273843..e009d21 100644 --- a/src/main/kotlin/com/ray650128/service/UserService.kt +++ b/src/main/kotlin/com/ray650128/service/UserService.kt @@ -7,11 +7,23 @@ class UserService { val userRepository = UserRepository() suspend fun addUser(data: User): Boolean { - return if (userRepository.get(data.account) == null) { + return if (userRepository.get(data) == null) { userRepository.add(data) true } else { false } } + + suspend fun getUser(data: User): User? { + return userRepository.get(data) + } + + suspend fun getUserByAccount(account: String): User? { + return userRepository.getByAccount(account) + } + + suspend fun updateUser(data: User) { + userRepository.update(data) + } } \ No newline at end of file diff --git a/src/main/kotlin/com/ray650128/utils/JwtConfig.kt b/src/main/kotlin/com/ray650128/utils/JwtConfig.kt new file mode 100644 index 0000000..13247ea --- /dev/null +++ b/src/main/kotlin/com/ray650128/utils/JwtConfig.kt @@ -0,0 +1,36 @@ +package com.ray650128.utils + +import com.auth0.jwt.JWT +import com.auth0.jwt.JWTVerifier +import com.auth0.jwt.algorithms.Algorithm +import com.ray650128.model.LoginBody +import io.ktor.server.application.* +import java.util.* + +object JwtConfig { + val secret = "my-secret" + val issuer = "com.ray650128" + val myRealm = "com.ray650128" + private val validityInMs = 36_000_00 * 24 // 1 day + private val algorithm = Algorithm.HMAC512(secret) + + val verifier: JWTVerifier = JWT + .require(algorithm) + .withIssuer(issuer) + .build() + + /** + * Produce a token for this combination of name and password + */ + fun generateToken(user: LoginBody): String = JWT.create() + .withSubject("Authentication") + .withIssuer(issuer) + .withClaim("account", user.account) + //.withExpiresAt(getExpiration()) // optional + .sign(algorithm) + + /** + * Calculate the expiration Date based on current time + the given validity + */ + private fun getExpiration() = Date(System.currentTimeMillis() + validityInMs) +} \ No newline at end of file diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 9bb9b45..6597638 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -8,7 +8,9 @@ ktor { } } jwt { - domain = "https://jwt-provider-domain/" + secret = "secret" + domain = "https://www.ray650128.com" + issuer = "https://www.ray650128.com/" audience = "jwt-audience" realm = "ktor sample app" }