實作註冊、登入API
This commit is contained in:
parent
4bea8421e6
commit
df2b630175
@ -1,6 +1,7 @@
|
||||
val ktor_version: String by project
|
||||
val kotlin_version: String by project
|
||||
val logback_version: String by project
|
||||
val kmongo_version: String by project
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.8.22"
|
||||
@ -32,4 +33,7 @@ dependencies {
|
||||
implementation("ch.qos.logback:logback-classic:$logback_version")
|
||||
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
||||
|
||||
implementation("org.litote.kmongo:kmongo:$kmongo_version")
|
||||
implementation("org.litote.kmongo:kmongo-id-serialization:$kmongo_version")
|
||||
}
|
||||
@ -2,3 +2,4 @@ ktor_version=2.3.1
|
||||
kotlin_version=1.8.22
|
||||
logback_version=1.2.11
|
||||
kotlin.code.style=official
|
||||
kmongo_version=4.5.0
|
||||
|
||||
36
src/main/kotlin/com/ray650128/JwtConfig.kt
Normal file
36
src/main/kotlin/com/ray650128/JwtConfig.kt
Normal file
@ -0,0 +1,36 @@
|
||||
package com.ray650128
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.JWTVerifier
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
import com.ray650128.model.User
|
||||
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 validityInMs = 300000 // 5 min
|
||||
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: User): 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)
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package com.ray650128.extensions
|
||||
|
||||
import com.ray650128.model.ErrorResponse
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
|
||||
|
||||
suspend fun <T> ApplicationCall.sendCreated(data: T?) {
|
||||
this.respond(
|
||||
status = HttpStatusCode.Created,
|
||||
message = data ?: mapOf("message" to "success.")
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun <T : Any> ApplicationCall.sendSuccess(data: T?) {
|
||||
this.respond(
|
||||
status = HttpStatusCode.OK,
|
||||
message = data ?: mapOf("message" to "success.")
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun ApplicationCall.sendUnauthorized() {
|
||||
this.respond(
|
||||
status = HttpStatusCode.Unauthorized,
|
||||
message = ErrorResponse.UNAUTHORIZED_RESPONSE
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun ApplicationCall.sendBadRequest(errorResponse: ErrorResponse) {
|
||||
this.respond(
|
||||
status = HttpStatusCode.BadRequest,
|
||||
message = errorResponse
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun ApplicationCall.sendNotFound() {
|
||||
this.respond(
|
||||
status = HttpStatusCode.NotFound,
|
||||
message = ErrorResponse.NOT_FOUND_RESPONSE
|
||||
)
|
||||
}
|
||||
12
src/main/kotlin/com/ray650128/model/ErrorResponse.kt
Normal file
12
src/main/kotlin/com/ray650128/model/ErrorResponse.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package com.ray650128.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ErrorResponse(val message: String) {
|
||||
companion object {
|
||||
val NOT_FOUND_RESPONSE = ErrorResponse("Person was not found")
|
||||
val BAD_REQUEST_RESPONSE = ErrorResponse("Invalid request")
|
||||
val UNAUTHORIZED_RESPONSE = ErrorResponse("Unauthorized")
|
||||
}
|
||||
}
|
||||
9
src/main/kotlin/com/ray650128/model/LoginResult.kt
Normal file
9
src/main/kotlin/com/ray650128/model/LoginResult.kt
Normal file
@ -0,0 +1,9 @@
|
||||
package com.ray650128.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class LoginResult(
|
||||
var account: String,
|
||||
var token: String
|
||||
)
|
||||
17
src/main/kotlin/com/ray650128/model/User.kt
Normal file
17
src/main/kotlin/com/ray650128/model/User.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package com.ray650128.model
|
||||
|
||||
import io.ktor.server.auth.*
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.litote.kmongo.Id
|
||||
import org.litote.kmongo.newId
|
||||
|
||||
@Serializable
|
||||
data class User(
|
||||
@Contextual val _id: Id<User>? = newId(),
|
||||
val account: String,
|
||||
val password: String,
|
||||
var name: String? = null,
|
||||
var createAt: Long? = null,
|
||||
var updatedAt: Long? = null
|
||||
): Principal
|
||||
@ -1,13 +1,105 @@
|
||||
package com.ray650128.plugins
|
||||
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.response.*
|
||||
import com.ray650128.JwtConfig
|
||||
import com.ray650128.extensions.sendBadRequest
|
||||
import com.ray650128.extensions.sendCreated
|
||||
import com.ray650128.extensions.sendSuccess
|
||||
import com.ray650128.extensions.sendUnauthorized
|
||||
import com.ray650128.model.ErrorResponse
|
||||
import com.ray650128.model.LoginResult
|
||||
import com.ray650128.model.User
|
||||
import com.ray650128.service.UserService
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Application.configureRouting() {
|
||||
|
||||
routing {
|
||||
get("/") {
|
||||
call.respondText("Hello World!")
|
||||
route("/api") {
|
||||
route("/v1") {
|
||||
post("/register") {
|
||||
val request = call.receive<User>()
|
||||
if (UserService.findByAccount(request.account) != null) {
|
||||
call.sendBadRequest(ErrorResponse("User has existed."))
|
||||
return@post
|
||||
}
|
||||
val newToken = JwtConfig.generateToken(request)
|
||||
val user = request.apply {
|
||||
createAt = System.currentTimeMillis()
|
||||
}
|
||||
UserService.create(user)?.let { userId ->
|
||||
call.response.headers.append("My-User-Id-Header", userId.toString())
|
||||
call.sendCreated(LoginResult(request.account, newToken))
|
||||
} ?: call.sendBadRequest(ErrorResponse.BAD_REQUEST_RESPONSE)
|
||||
}
|
||||
|
||||
post("/login") {
|
||||
val request = call.receive<User>()
|
||||
val user = UserService.findByLoginInfo(request.account, request.password)
|
||||
if (user != null) {
|
||||
val token = JwtConfig.generateToken(request)
|
||||
call.sendSuccess(LoginResult(request.account, token))
|
||||
} else {
|
||||
call.sendBadRequest(ErrorResponse("Account or Password wrong."))
|
||||
}
|
||||
}
|
||||
|
||||
authenticate {
|
||||
post("/logout") {
|
||||
val account = call.authentication.principal<User>()?.account ?: run {
|
||||
call.sendUnauthorized()
|
||||
return@post
|
||||
}
|
||||
val user = UserService.findByAccount(account) ?: run {
|
||||
call.sendUnauthorized()
|
||||
return@post
|
||||
}
|
||||
UserService.updateById(user._id.toString(), user)
|
||||
call.sendSuccess(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*get {
|
||||
val peopleList = UserService.findAll()
|
||||
call.respond(peopleList)
|
||||
}
|
||||
|
||||
get("/{id}") {
|
||||
val id = call.parameters["id"].toString()
|
||||
UserService.findById(id)
|
||||
?.let { foundPerson -> call.respond(foundPerson) }
|
||||
?: call.sendNotFound()
|
||||
}
|
||||
|
||||
get("/search") {
|
||||
val name = call.request.queryParameters["name"].toString()
|
||||
val foundPeople = UserService.findByName(name)
|
||||
call.respond(foundPeople)
|
||||
}
|
||||
|
||||
put("/{id}") {
|
||||
val id = call.parameters["id"].toString()
|
||||
val userRequest = call.receive<User>()
|
||||
val updatedSuccessfully = UserService.updateById(id, userRequest)
|
||||
if (updatedSuccessfully) {
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
} else {
|
||||
call.respond(HttpStatusCode.BadRequest, ErrorResponse.BAD_REQUEST_RESPONSE)
|
||||
}
|
||||
}
|
||||
|
||||
delete("/{id}") {
|
||||
val id = call.parameters["id"].toString()
|
||||
val deletedSuccessfully = UserService.deleteById(id)
|
||||
if (deletedSuccessfully) {
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound, ErrorResponse.NOT_FOUND_RESPONSE)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
56
src/main/kotlin/com/ray650128/service/UserService.kt
Normal file
56
src/main/kotlin/com/ray650128/service/UserService.kt
Normal file
@ -0,0 +1,56 @@
|
||||
package com.ray650128.service
|
||||
|
||||
import com.ray650128.model.User
|
||||
import org.bson.types.ObjectId
|
||||
import org.litote.kmongo.*
|
||||
import org.litote.kmongo.id.toId
|
||||
|
||||
object UserService {
|
||||
private val client = KMongo.createClient("mongodb://ray650128:Zx650128!@www.ray650128.com:27017")
|
||||
private val database = client.getDatabase("test_system")
|
||||
private val userCollection = database.getCollection<User>()
|
||||
|
||||
fun create(user: User): Id<User>? {
|
||||
userCollection.insertOne(user)
|
||||
return user._id
|
||||
}
|
||||
|
||||
fun findAll(): List<User> = userCollection.find().toList()
|
||||
|
||||
fun findById(id: String): User? {
|
||||
val bsonId: Id<User> = ObjectId(id).toId()
|
||||
return userCollection.findOne(User::_id eq bsonId)
|
||||
}
|
||||
|
||||
fun findByName(name: String): List<User> {
|
||||
val caseSensitiveTypeSafeFilter = User::name regex name
|
||||
return userCollection.find(caseSensitiveTypeSafeFilter).toList()
|
||||
}
|
||||
|
||||
fun findByAccount(account: String): User? {
|
||||
return userCollection.findOne(User::account eq account)
|
||||
}
|
||||
|
||||
fun findByLoginInfo(account: String, password: String): User? {
|
||||
return userCollection.findOne(
|
||||
User::account eq account,
|
||||
User::password eq password
|
||||
)
|
||||
}
|
||||
|
||||
fun updateById(id: String, request: User): Boolean =
|
||||
findById(id)?.let { user ->
|
||||
val updateResult = userCollection.replaceOne(
|
||||
user.copy(
|
||||
name = request.name,
|
||||
updatedAt = request.updatedAt
|
||||
)
|
||||
)
|
||||
updateResult.modifiedCount == 1L
|
||||
} ?: false
|
||||
|
||||
fun deleteById(id: String): Boolean {
|
||||
val deleteResult = userCollection.deleteOneById(ObjectId(id))
|
||||
return deleteResult.deletedCount == 1L
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user