mirror of https://github.com/usbharu/Hideout.git
refactor: Spring SecurityのコンフィグをKotlin DSLで記述するように
This commit is contained in:
parent
6af7cd67c8
commit
56458fc53c
|
@ -13,28 +13,29 @@ import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.Htt
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsImpl
|
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsImpl
|
||||||
import dev.usbharu.hideout.core.query.UserQueryService
|
import dev.usbharu.hideout.core.query.UserQueryService
|
||||||
import dev.usbharu.hideout.util.RsaUtil
|
import dev.usbharu.hideout.util.RsaUtil
|
||||||
|
import dev.usbharu.hideout.util.hasAnyScope
|
||||||
import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner
|
import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner
|
||||||
import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser
|
import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser
|
||||||
import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier
|
import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer
|
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer
|
||||||
import org.springframework.boot.autoconfigure.security.servlet.PathRequest
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.context.annotation.Primary
|
import org.springframework.context.annotation.Primary
|
||||||
import org.springframework.core.annotation.Order
|
import org.springframework.core.annotation.Order
|
||||||
import org.springframework.http.HttpMethod
|
import org.springframework.http.HttpMethod.GET
|
||||||
|
import org.springframework.http.HttpMethod.POST
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
|
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
|
||||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
|
||||||
import org.springframework.security.authentication.AccountStatusUserDetailsChecker
|
import org.springframework.security.authentication.AccountStatusUserDetailsChecker
|
||||||
import org.springframework.security.authentication.AuthenticationManager
|
import org.springframework.security.authentication.AuthenticationManager
|
||||||
import org.springframework.security.config.Customizer
|
|
||||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
|
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.invoke
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy
|
import org.springframework.security.config.http.SessionCreationPolicy
|
||||||
import org.springframework.security.core.Authentication
|
import org.springframework.security.core.Authentication
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||||
|
@ -51,15 +52,14 @@ import org.springframework.security.web.authentication.AuthenticationEntryPointF
|
||||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint
|
import org.springframework.security.web.authentication.HttpStatusEntryPoint
|
||||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider
|
||||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher
|
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher
|
import org.springframework.security.web.util.matcher.AnyRequestMatcher
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector
|
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.interfaces.RSAPrivateKey
|
import java.security.interfaces.RSAPrivateKey
|
||||||
import java.security.interfaces.RSAPublicKey
|
import java.security.interfaces.RSAPublicKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@EnableWebSecurity(debug = false)
|
@EnableWebSecurity(debug = true)
|
||||||
@Configuration
|
@Configuration
|
||||||
@Suppress("FunctionMaxLength", "TooManyFunctions")
|
@Suppress("FunctionMaxLength", "TooManyFunctions")
|
||||||
class SecurityConfig {
|
class SecurityConfig {
|
||||||
|
@ -75,40 +75,26 @@ class SecurityConfig {
|
||||||
@Order(1)
|
@Order(1)
|
||||||
fun httpSignatureFilterChain(
|
fun httpSignatureFilterChain(
|
||||||
http: HttpSecurity,
|
http: HttpSecurity,
|
||||||
httpSignatureFilter: HttpSignatureFilter,
|
httpSignatureFilter: HttpSignatureFilter
|
||||||
introspector: HandlerMappingIntrospector
|
|
||||||
): SecurityFilterChain {
|
): SecurityFilterChain {
|
||||||
val builder = MvcRequestMatcher.Builder(introspector)
|
http {
|
||||||
http
|
securityMatcher("/users/*/posts/*")
|
||||||
.securityMatcher("/users/*/posts/*")
|
addFilterAt<RequestCacheAwareFilter>(httpSignatureFilter)
|
||||||
.addFilter(httpSignatureFilter)
|
addFilterBefore<HttpSignatureFilter>(ExceptionTranslationFilter(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
|
||||||
.addFilterBefore(
|
authorizeHttpRequests {
|
||||||
ExceptionTranslationFilter(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)),
|
authorize(anyRequest, permitAll)
|
||||||
HttpSignatureFilter::class.java
|
|
||||||
)
|
|
||||||
.authorizeHttpRequests {
|
|
||||||
it.requestMatchers(
|
|
||||||
builder.pattern("/inbox"),
|
|
||||||
builder.pattern("/outbox"),
|
|
||||||
builder.pattern("/users/*/inbox"),
|
|
||||||
builder.pattern("/users/*/outbox")
|
|
||||||
).authenticated()
|
|
||||||
it.anyRequest().permitAll()
|
|
||||||
}
|
}
|
||||||
.csrf {
|
exceptionHandling {
|
||||||
it.disable()
|
authenticationEntryPoint = HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)
|
||||||
}
|
defaultAuthenticationEntryPointFor(
|
||||||
.exceptionHandling {
|
|
||||||
it.authenticationEntryPoint(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
|
|
||||||
it.defaultAuthenticationEntryPointFor(
|
|
||||||
HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
|
HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
|
||||||
AnyRequestMatcher.INSTANCE
|
AnyRequestMatcher.INSTANCE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.sessionManagement {
|
sessionManagement {
|
||||||
it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
sessionCreationPolicy = SessionCreationPolicy.STATELESS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.build()
|
return http.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,59 +138,65 @@ class SecurityConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(2)
|
@Order(2)
|
||||||
fun oauth2SecurityFilterChain(http: HttpSecurity, introspector: HandlerMappingIntrospector): SecurityFilterChain {
|
fun oauth2SecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
val builder = MvcRequestMatcher.Builder(introspector)
|
|
||||||
|
|
||||||
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
|
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
|
||||||
http.exceptionHandling {
|
http {
|
||||||
it.authenticationEntryPoint(
|
exceptionHandling {
|
||||||
LoginUrlAuthenticationEntryPoint("/login")
|
authenticationEntryPoint = LoginUrlAuthenticationEntryPoint("/login")
|
||||||
)
|
}
|
||||||
}.oauth2ResourceServer {
|
oauth2ResourceServer {
|
||||||
it.jwt(Customizer.withDefaults())
|
jwt {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return http.build()
|
return http.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(4)
|
@Order(4)
|
||||||
fun defaultSecurityFilterChain(http: HttpSecurity, introspector: HandlerMappingIntrospector): SecurityFilterChain {
|
fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
val builder = MvcRequestMatcher.Builder(introspector)
|
http {
|
||||||
|
authorizeHttpRequests {
|
||||||
|
|
||||||
http.authorizeHttpRequests {
|
authorize("/error", permitAll)
|
||||||
it.requestMatchers(PathRequest.toH2Console()).permitAll()
|
authorize("/login", permitAll)
|
||||||
it.requestMatchers(
|
authorize(GET, "/.well-known/**", permitAll)
|
||||||
builder.pattern("/inbox"),
|
authorize(GET, "/nodeinfo/2.0", permitAll)
|
||||||
builder.pattern("/users/*/inbox"),
|
|
||||||
builder.pattern("/api/v1/apps"),
|
authorize(POST, "/inbox", permitAll)
|
||||||
builder.pattern("/api/v1/instance/**"),
|
authorize(POST, "/users/*/inbox", permitAll)
|
||||||
builder.pattern("/.well-known/**"),
|
|
||||||
builder.pattern("/error"),
|
authorize(POST, "/api/v1/apps", permitAll)
|
||||||
builder.pattern("/nodeinfo/2.0"),
|
authorize(GET, "/api/v1/instance/**", permitAll)
|
||||||
builder.pattern("/api/v1/accounts")
|
authorize(POST, "/api/v1/accounts", permitAll)
|
||||||
).permitAll()
|
|
||||||
it.requestMatchers(
|
authorize("/auth/sign_up", hasRole("ANONYMOUS"))
|
||||||
builder.pattern("/auth/**")
|
|
||||||
).anonymous()
|
authorize(GET, "/api/v1/accounts/verify_credentials", hasAnyScope("read", "read:accounts"))
|
||||||
it.requestMatchers(builder.pattern("/change-password")).authenticated()
|
|
||||||
it.requestMatchers(builder.pattern("/api/v1/accounts/verify_credentials"))
|
authorize(POST, "/api/v1/media", hasAnyScope("write", "write:media"))
|
||||||
.hasAnyAuthority("SCOPE_read", "SCOPE_read:accounts")
|
authorize(POST, "/api/v1/statuses", hasAnyScope("write", "write:statuses"))
|
||||||
it.requestMatchers(builder.pattern(HttpMethod.POST, "/api/v1/media"))
|
|
||||||
.hasAnyAuthority("SCOPE_write", "SCOPE_write:media")
|
authorize(anyRequest, authenticated)
|
||||||
it.requestMatchers(builder.pattern(HttpMethod.POST, "/api/v1/statuses"))
|
}
|
||||||
.hasAnyAuthority("SCOPE_write", "SCOPE_write:statuses")
|
|
||||||
it.anyRequest().authenticated()
|
oauth2ResourceServer {
|
||||||
|
jwt { }
|
||||||
|
}
|
||||||
|
|
||||||
|
formLogin {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
csrf {
|
||||||
|
ignoringRequestMatchers("/users/*/inbox", "/inbox", "/api/v1/apps", "/api/v1/accounts")
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
frameOptions {
|
||||||
|
sameOrigin = true
|
||||||
}
|
}
|
||||||
http.oauth2ResourceServer {
|
|
||||||
it.jwt(Customizer.withDefaults())
|
|
||||||
}.passwordManagement { }.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())
|
|
||||||
}.headers {
|
|
||||||
it.frameOptions { frameOptionsConfig ->
|
|
||||||
frameOptionsConfig.sameOrigin()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return http.build()
|
return http.build()
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package dev.usbharu.hideout.util
|
||||||
|
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager
|
||||||
|
import org.springframework.security.config.annotation.web.AuthorizeHttpRequestsDsl
|
||||||
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
|
||||||
|
|
||||||
|
fun AuthorizeHttpRequestsDsl.hasScope(scope: String): AuthorizationManager<RequestAuthorizationContext> =
|
||||||
|
hasAuthority("SCOPE_$scope")
|
||||||
|
|
||||||
|
fun AuthorizeHttpRequestsDsl.hasAnyScope(vararg scopes: String): AuthorizationManager<RequestAuthorizationContext> =
|
||||||
|
hasAnyAuthority(
|
||||||
|
*scopes.map { "SCOPE_$it" }.toTypedArray()
|
||||||
|
)
|
|
@ -36,7 +36,7 @@ spring:
|
||||||
max-request-size: 40MB
|
max-request-size: 40MB
|
||||||
h2:
|
h2:
|
||||||
console:
|
console:
|
||||||
enabled: true
|
enabled: false
|
||||||
server:
|
server:
|
||||||
tomcat:
|
tomcat:
|
||||||
basedir: tomcat
|
basedir: tomcat
|
||||||
|
|
Loading…
Reference in New Issue