일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 도커
- 스프링시큐리티
- 스프링
- 소셜로그인
- 재갱신
- 오블완
- 프로그래머스
- 메시지
- 파이썬
- githubactions
- CI/CD
- java
- springsecurityoauth2client
- oauth2
- 트랜잭션
- springsecurity
- Spring
- springdataredis
- 토이프로젝트
- 액세스토큰
- docker
- 백준
- 데이터베이스
- 스프링부트
- AWS
- 국제화
- 리프레시토큰
- yaml-resource-bundle
- 티스토리챌린지
- JIRA
- Today
- Total
땃쥐네
[Spring Security] 스프링 시큐리티와 코틀린 DSL 본문
자바, 순수 코틀린 문법을 사용하다보면 스프링 시큐리티 필터체인을 작성할 때 다음과 같이 작성해야한다.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
물론 이것도 괜찮긴 한데... 람다 표현식을 체이닝을 통해 넣어주는 식이기도 하고 가독성이 썩 좋지는 않다.
import org.springframework.security.config.annotation.web.invoke
코틀린을 쓴다면 이 import문을 Security 관련 Config 클래스 파일에 삽입해보자.
IDE의 지원을 받아서 자동 작성할 수 없다보니, 개발자가 수동으로 import문을 넣어줘야한다.
자주 사용한다면 IntelliJ의 Live Template 에 추가해서 사용하는 것도 괜찮은 선택이 될 수 있다.
@Bean
@Order(0)
fun ajaxSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
securityMatcher("/api/**", "/admin/api/**")
authorizeHttpRequests {
authorize("/admin/api/**", hasRole("ADMIN"))
authorize("/api/login", permitAll)
authorize("/api/users", permitAll)
authorize("/api/messages", hasRole("MANAGER"))
authorize(anyRequest, authenticated)
}
addFilterBefore<UsernamePasswordAuthenticationFilter>(ajaxLoginProcessingFilter())
exceptionHandling {
authenticationEntryPoint = ajaxAuthenticationEntryPoint()
accessDeniedHandler = ajaxAccessDeniedHandler()
}
}
return http.build()
}
위의 내용을 import하면, 스프링 시큐리티를 위한 DSL을 사용할 수 있게 되고, 위와 같이 시큐리티 설정 코드를 작성할 수 있게 된다.
순수 java, kotlin 문법을 사용하면 설정 클래스 가독성이 개인적으로 좋아보이지 않았는데, 개인적으로는 이쪽을 쓰는게 가독성에서 더 좋다.
fun authorizeRequests(authorizeRequestsConfiguration: AuthorizeRequestsDsl.() -> Unit) {
val authorizeRequestsCustomizer = AuthorizeRequestsDsl().apply(authorizeRequestsConfiguration).get()
this.http.authorizeRequests(authorizeRequestsCustomizer)
}
참고로 여기서 authorizeHttpRequests 와 같은 부분을 IntelliJ에서 Ctrl + 클릭을 통해 해당 코드를 따라가보면 이런 코드를 확인할 수 있는데, AuthorizeRequestDsl을 Ctrl+클릭해보자.
/**
* A Kotlin DSL to configure [HttpSecurity] request authorization using idiomatic Kotlin code.
*
* @author Eleftheria Stein
* @since 5.3
*/
class AuthorizeRequestsDsl : AbstractRequestMatcherDsl() {
private val authorizationRules = mutableListOf<AuthorizationRule>()
private val HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
private val MVC_PRESENT = ClassUtils.isPresent(
HANDLER_MAPPING_INTROSPECTOR,
AuthorizeRequestsDsl::class.java.classLoader)
private val PATTERN_TYPE = if (MVC_PRESENT) PatternType.MVC else PatternType.ANT
/**
* Adds a request authorization rule.
*
* @param matches the [RequestMatcher] to match incoming requests against
* @param access the SpEL expression to secure the matching request
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
*/
fun authorize(matches: RequestMatcher = AnyRequestMatcher.INSTANCE,
access: String) {
authorizationRules.add(MatcherAuthorizationRule(matches, access))
}
/**
* Adds a request authorization rule for an endpoint matching the provided
* pattern.
* If Spring MVC is on the classpath, it will use an MVC matcher.
* If Spring MVC is not on the classpath, it will use an ant matcher.
* The MVC will use the same rules that Spring MVC uses for matching.
* For example, often times a mapping of the path "/path" will match on
* "/path", "/path/", "/path.html", etc.
* If the current request will not be processed by Spring MVC, a reasonable default
* using the pattern as an ant pattern will be used.
*
* @param pattern the pattern to match incoming requests against.
* @param access the SpEL expression to secure the matching request
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
*/
fun authorize(pattern: String, access: String) {
authorizationRules.add(PatternAuthorizationRule(pattern = pattern,
patternType = PATTERN_TYPE,
rule = access))
}
이렇게 따라가보면 DSL Api를 사용하는 방법들을 설명해놓아서 가끔 궁금해질 때 있으면 꺼내서 쓰기 좋다.
쓰면서 느낀 점(2024.05.27 시점)
- 스프링 시큐리티 공식 문서에서 kotlin 버전에 대한 설명은 kotlindsl로 설명되어 있기도 하고 처음에는 괜찮다고 느낀 방식이이였다.
- 하지만 쓰다보니 기술이 성숙화되지 않았다는 느낌을 받았다.
- 예를 들면 JavaDSL 방식과 다르게 동작하는 기능이 존재한다. Spring Security 6.3 기준 여러군데 존재한다. (RoleHierarchy 빈을 KotlinDSL 방식에서 사용하지 않는 문제, GrantedAuthorityDefaults 빈을 KotlinDSL 방식에서 사용하지 않는 문제)
- 이런 부분에 대해서는 Issue를 몇 개 올렸는데 스프링 시큐리티 6.4에 반영될 예정이라고 한다. 즉 지금 시점에 놓고보면 실제 배포되고 안정화되기 까지 시간이 좀 걸릴 수 있다.
- 그렇다고 KotlinDSL 방식을 무조건 버리기도 애매하다. 잘 사용되는 기능에 한해서는 코드 작성이 깔끔해지는 장점이 정말 크기 때문이다. 다만 위의 사례처럼 예상과 다르게 작동하는 기능이 일부 존재하다보니, 우선 기능을 적용해보고 통합 테스트를 충분히 거치고 사용하지 말 지 결정하는게 나을 것 같다.
'Spring > Spring Security' 카테고리의 다른 글
[Spring Security] 컨트롤러에서 Authentication 파라미터 사용 (0) | 2024.06.12 |
---|---|
[Spring Security] 스프링부트 시큐리티 의존성 추가로 일어나는 일들 (0) | 2024.03.09 |