加入登出功能

This commit is contained in:
Raymond Yang 2022-05-24 14:31:25 +08:00
parent 9237736067
commit 64c987f6c0
7 changed files with 163 additions and 15 deletions

View File

@ -12,5 +12,4 @@ fun Application.module() {
DatabaseFactory.init()
DatabaseFactory.createTable()
configureRouting()
configureSecurity()
}

View File

@ -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

View File

@ -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<LoginBody>()
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<LoginBody>()
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<User>()?.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.")
)
}
}
}
}
}
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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"
}