version 1.0

This commit is contained in:
2024-12-23 16:00:14 +08:00
parent 13f7ecc88a
commit 688a196a52
29 changed files with 1951 additions and 433 deletions

View File

@@ -11,11 +11,18 @@ import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.HttpResponseStatus
import io.netty.handler.codec.http.HttpVersion
import io.netty.util.ReferenceCountUtil
import java.security.SecureRandom
import java.security.spec.KeySpec
import java.util.Base64
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
abstract class AbstractNettyHttpAuthenticator(private val authorizer : Authorizer)
: ChannelInboundHandlerAdapter() {
private companion object {
companion object {
private const val KEY_LENGTH = 256
private val AUTHENTICATION_FAILED: FullHttpResponse = DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED, Unpooled.EMPTY_BUFFER).apply {
headers()[HttpHeaderNames.CONTENT_LENGTH] = "0"
@@ -25,13 +32,59 @@ abstract class AbstractNettyHttpAuthenticator(private val authorizer : Authorize
HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN, Unpooled.EMPTY_BUFFER).apply {
headers()[HttpHeaderNames.CONTENT_LENGTH] = "0"
}
private fun concat(arr1: ByteArray, arr2: ByteArray): ByteArray {
val result = ByteArray(arr1.size + arr2.size)
var j = 0
for(element in arr1) {
result[j] = element
j += 1
}
for(element in arr2) {
result[j] = element
j += 1
}
return result
}
fun hashPassword(password : String, salt : String? = null) : String {
val actualSalt = salt?.let(Base64.getDecoder()::decode) ?: SecureRandom().run {
val result = ByteArray(16)
nextBytes(result)
result
}
val spec: KeySpec = PBEKeySpec(password.toCharArray(), actualSalt, 10, KEY_LENGTH)
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val hash = factory.generateSecret(spec).encoded
return String(Base64.getEncoder().encode(concat(hash, actualSalt)))
}
// fun decodePasswordHash(passwordHash : String) : Pair<String, String> {
// return passwordHash.indexOf(':')
// .takeIf { it > 0 }
// ?.let { sep ->
// passwordHash.substring(0, sep) to passwordHash.substring(sep)
// } ?: throw IllegalArgumentException("Failed to decode password hash")
// }
fun decodePasswordHash(passwordHash : String) : Pair<ByteArray, ByteArray> {
val decoded = Base64.getDecoder().decode(passwordHash)
val hash = ByteArray(KEY_LENGTH / 8)
val salt = ByteArray(decoded.size - KEY_LENGTH / 8)
System.arraycopy(decoded, 0, hash, 0, hash.size)
System.arraycopy(decoded, hash.size, salt, 0, salt.size)
return hash to salt
}
}
abstract fun authenticate(ctx : ChannelHandlerContext, req : HttpRequest) : String?
abstract fun authenticate(ctx : ChannelHandlerContext, req : HttpRequest) : Set<Role>?
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
if(msg is HttpRequest) {
val user = authenticate(ctx, msg) ?: return authenticationFailure(ctx, msg)
val authorized = authorizer.authorize(user, msg)
val roles = authenticate(ctx, msg) ?: return authenticationFailure(ctx, msg)
val authorized = authorizer.authorize(roles, msg)
if(authorized) {
super.channelRead(ctx, msg)
} else {