mirror of https://github.com/usbharu/Hideout.git
fix: JWTトークンの有効期限を設定
This commit is contained in:
parent
d3a84b3157
commit
c880e9c5d1
|
@ -6,12 +6,14 @@ import com.nimbusds.jose.jwk.source.ImmutableJWKSet
|
|||
import com.nimbusds.jose.jwk.source.JWKSource
|
||||
import com.nimbusds.jose.proc.SecurityContext
|
||||
import dev.usbharu.hideout.domain.model.UserDetailsImpl
|
||||
import dev.usbharu.hideout.util.RsaUtil
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.boot.autoconfigure.security.servlet.PathRequest
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.security.config.Customizer
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
|
@ -33,7 +35,7 @@ import java.security.interfaces.RSAPrivateKey
|
|||
import java.security.interfaces.RSAPublicKey
|
||||
import java.util.*
|
||||
|
||||
@EnableWebSecurity(debug = false)
|
||||
@EnableWebSecurity(debug = true)
|
||||
@Configuration
|
||||
class SecurityConfig {
|
||||
|
||||
|
@ -88,6 +90,7 @@ class SecurityConfig {
|
|||
.formLogin(Customizer.withDefaults())
|
||||
.csrf {
|
||||
it.ignoringRequestMatchers(builder.pattern("/users/*/inbox"))
|
||||
it.ignoringRequestMatchers(builder.pattern(HttpMethod.POST, "/api/v1/apps"))
|
||||
it.ignoringRequestMatchers(builder.pattern("/inbox"))
|
||||
it.ignoringRequestMatchers(PathRequest.toH2Console())
|
||||
}
|
||||
|
@ -103,6 +106,7 @@ class SecurityConfig {
|
|||
fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = ["hideout.security.jwt.generate"], havingValue = "false", matchIfMissing = true)
|
||||
fun genJwkSource(): JWKSource<SecurityContext> {
|
||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||
keyPairGenerator.initialize(2048)
|
||||
|
@ -122,8 +126,8 @@ class SecurityConfig {
|
|||
@Bean
|
||||
@ConditionalOnProperty(name = ["hideout.security.jwt.generate"], havingValue = "")
|
||||
fun loadJwkSource(jwkConfig: JwkConfig): JWKSource<SecurityContext> {
|
||||
val rsaKey = RSAKey.Builder(jwkConfig.publicKey)
|
||||
.privateKey(jwkConfig.privateKey)
|
||||
val rsaKey = RSAKey.Builder(RsaUtil.decodeRsaPublicKey(jwkConfig.publicKey))
|
||||
.privateKey(RsaUtil.decodeRsaPrivateKey(jwkConfig.privateKey))
|
||||
.keyID(jwkConfig.keyId)
|
||||
.build()
|
||||
return ImmutableJWKSet(JWKSet(rsaKey))
|
||||
|
@ -157,6 +161,6 @@ class SecurityConfig {
|
|||
@ConditionalOnProperty(name = ["hideout.security.jwt.generate"], havingValue = "")
|
||||
data class JwkConfig(
|
||||
val keyId: String,
|
||||
val publicKey: RSAPublicKey,
|
||||
val privateKey: RSAPrivateKey
|
||||
val publicKey: String,
|
||||
val privateKey: String
|
||||
)
|
||||
|
|
|
@ -3,19 +3,24 @@ package dev.usbharu.hideout.controller.mastodon
|
|||
import dev.usbharu.hideout.controller.mastodon.generated.StatusApi
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequest
|
||||
import dev.usbharu.hideout.domain.model.UserDetailsImpl
|
||||
import dev.usbharu.hideout.service.api.mastodon.StatusesApiService
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.oauth2.jwt.Jwt
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.web.bind.annotation.ModelAttribute
|
||||
|
||||
@Controller
|
||||
class MastodonStatusesApiContoller(private val statusesApiService: StatusesApiService) : StatusApi {
|
||||
override fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity<Status> = runBlocking {
|
||||
val principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal()
|
||||
require(principal is UserDetailsImpl)
|
||||
ResponseEntity(statusesApiService.postStatus(statusesRequest, principal), HttpStatus.OK)
|
||||
override fun apiV1StatusesPost(@ModelAttribute statusesRequest: StatusesRequest): ResponseEntity<Status> =
|
||||
runBlocking {
|
||||
val jwt = SecurityContextHolder.getContext().authentication.principal as Jwt
|
||||
|
||||
ResponseEntity(
|
||||
statusesApiService.postStatus(statusesRequest, jwt.getClaim<String>("uid").toLong()),
|
||||
HttpStatus.OK
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,10 @@ import org.springframework.security.oauth2.core.ClientAuthenticationMethod
|
|||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository
|
||||
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings
|
||||
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
||||
@Service
|
||||
|
@ -40,6 +43,13 @@ class AppApiServiceImpl(
|
|||
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
|
||||
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||
.redirectUri(appsRequest.redirectUris)
|
||||
.tokenSettings(
|
||||
TokenSettings.builder()
|
||||
.accessTokenTimeToLive(
|
||||
Duration.ofSeconds((Instant.MAX.epochSecond - Instant.now().epochSecond - 10000) / 1000)
|
||||
)
|
||||
.build()
|
||||
)
|
||||
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
|
||||
.scopes { it.addAll(parseScope(appsRequest.scopes.orEmpty())) }
|
||||
.build()
|
||||
|
|
|
@ -2,12 +2,12 @@ package dev.usbharu.hideout.service.api.mastodon
|
|||
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequest
|
||||
import dev.usbharu.hideout.domain.model.UserDetailsImpl
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.query.PostQueryService
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.service.mastodon.AccountService
|
||||
import dev.usbharu.hideout.service.post.PostService
|
||||
import org.springframework.stereotype.Service
|
||||
|
@ -15,7 +15,7 @@ import java.time.Instant
|
|||
|
||||
@Service
|
||||
interface StatusesApiService {
|
||||
suspend fun postStatus(statusesRequest: StatusesRequest, user: UserDetailsImpl): Status
|
||||
suspend fun postStatus(statusesRequest: StatusesRequest, userId: Long): Status
|
||||
}
|
||||
|
||||
@Service
|
||||
|
@ -23,11 +23,12 @@ class StatsesApiServiceImpl(
|
|||
private val postService: PostService,
|
||||
private val accountService: AccountService,
|
||||
private val postQueryService: PostQueryService,
|
||||
private val userQueryService: UserQueryService
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
) :
|
||||
StatusesApiService {
|
||||
@Suppress("LongMethod")
|
||||
override suspend fun postStatus(statusesRequest: StatusesRequest, user: UserDetailsImpl): Status {
|
||||
override suspend fun postStatus(statusesRequest: StatusesRequest, userId: Long): Status = transaction.transaction {
|
||||
val visibility = when (statusesRequest.visibility) {
|
||||
StatusesRequest.Visibility.public -> Visibility.PUBLIC
|
||||
StatusesRequest.Visibility.unlisted -> Visibility.UNLISTED
|
||||
|
@ -43,10 +44,10 @@ class StatsesApiServiceImpl(
|
|||
visibility = visibility,
|
||||
repostId = null,
|
||||
repolyId = statusesRequest.inReplyToId?.toLongOrNull(),
|
||||
userId = user.id
|
||||
userId = userId
|
||||
)
|
||||
)
|
||||
val account = accountService.findById(user.id)
|
||||
val account = accountService.findById(userId)
|
||||
|
||||
val postVisibility = when (statusesRequest.visibility) {
|
||||
StatusesRequest.Visibility.public -> Status.Visibility.public
|
||||
|
@ -66,7 +67,7 @@ class StatsesApiServiceImpl(
|
|||
null
|
||||
}
|
||||
|
||||
return Status(
|
||||
Status(
|
||||
id = post.id.toString(),
|
||||
uri = post.apId,
|
||||
createdAt = Instant.ofEpochMilli(post.createdAt).toString(),
|
||||
|
|
|
@ -7,6 +7,12 @@ hideout:
|
|||
password: ""
|
||||
job-queue:
|
||||
type: "nosql"
|
||||
security:
|
||||
jwt:
|
||||
generate: true
|
||||
key-id: a
|
||||
private-key: "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ=="
|
||||
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB"
|
||||
spring:
|
||||
jackson:
|
||||
serialization:
|
||||
|
|
|
@ -137,6 +137,9 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StatusesRequest"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StatusesRequest"
|
||||
responses:
|
||||
200:
|
||||
description: 成功
|
||||
|
|
Loading…
Reference in New Issue