๊ด€๋ฆฌ ๋ฉ”๋‰ด

JiYoung Dev ๐Ÿ–ฅ

Spring Security ์ ์šฉ๊ธฐ (7) Spring Security Authorization(Method Security) ๋ณธ๋ฌธ

Study/Java

Spring Security ์ ์šฉ๊ธฐ (7) Spring Security Authorization(Method Security)

Shinjio 2024. 5. 6. 21:09

์ง€๋‚œ๋ฒˆ์— ์ด์–ด ์ด๋ฒˆ์—๋Š” Authorization์˜ Method Security์™€ ์ถ”๊ฐ€ ๋‚ด์šฉ์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ–ˆ๋‹ค. 

2024.05.05 - [Study/Java] - Spring Security ์ ์šฉ๊ธฐ (6) Spring Security Authorization(HttpServletRequests)

 

Spring Security ์ ์šฉ๊ธฐ (6) Spring Security Authorization(HttpServletRequests)

2024.05.05 - [Study/Java] - Spring Security ์ ์šฉ๊ธฐ (5) Spring Security Authentication ๋™์ž‘์›๋ฆฌ Spring Security ์ ์šฉ๊ธฐ (5) Spring Security Authentication ๋™์ž‘์›๋ฆฌjwt์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•˜๊ณ  ์ธ์ฆ ๋ฐ ์ธ๊ฐ€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ ค๋˜ ์ค‘ sprin

danyoujeong.tistory.com

 

Spring Security ๊ณต์‹ ๋ฌธ์„œ

https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html

 

Method Security :: Spring Security

As already noted, there is a Spring AOP method interceptor for each annotation, and each of these has a location in the Spring AOP advisor chain. Namely, the @PreFilter method interceptor’s order is 100, @PreAuthorize's is 200, and so on. The reason this

docs.spring.io


๐ŸŽ Method Security ๊ฐœ์š”

์ด์ „ ์‹œ๊ฐ„์— ๋“ฑ์žฅํ–ˆ๋˜ request level์— ๋Œ€ํ•œ ์ธ๊ฐ€ ๋ฐฉ์‹์— ์ด์–ด Spring Security๋Š” method level์—์„œ์˜ ๋ชจ๋ธ๋„ ์ง€์›ํ•œ๋‹ค. 

 

method ์ˆ˜์ค€์˜ Spring Security๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” @Configuration ํด๋ž˜์Šค์— @EnableMethodSecurity๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ฆ‰์‹œ, @PreAuthorize, @PostAuthorize, @PreFilter, @PostFilter์™€ ๊ฐ™์€ ๋ฉ”์†Œ๋“œ ์ธ๊ฐ€ ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. 


๐ŸŽ Method Security ๋™์ž‘ ์›๋ฆฌ

Spring Security์˜ ๋ฉ”์†Œ๋“œ ์ธ๊ฐ€ ์ง€์›์€ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฝ์šฐ์— ํŽธ๋ฆฌํ•˜๋‹ค. 

 

1. ์„ธ๋ฐ€ํ•œ ์ธ๊ฐ€ ๋กœ์ง ์ถ”์ถœ : ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฉ”์†Œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ๋ฐ˜ํ™˜๊ฐ’์ด ์ธ๊ฐ€ ๊ฒฐ์ •์— ๊ธฐ์—ฌํ•˜๋Š” ๊ฒฝ์šฐ์ด๋‹ค. 

2. ์„œ๋น„์Šค ๋ ˆ์ด์–ด์—์„œ ๋ณด์•ˆ ๊ฐ•์ œ : ์„œ๋น„์Šค ๋ ˆ์ด์–ด์—์„œ ์ง์ ‘์ ์œผ๋กœ ๋ณด์•ˆ์„ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ๋‹ค. 

3. ์Šคํƒ€์ผ์ ์œผ๋กœ ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ๊ตฌ์„ฑ์„ HttpSecurity ๊ธฐ๋ฐ˜ ๊ตฌ์„ฑ๋ณด๋‹ค ์„ ํ˜ธํ•  ๋•Œ

4. Spring AOP๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์†Œ๋“œ ๋ณด์•ˆ์ด ๊ตฌ์ถ•๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— Spring Security์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ํ•„์š”์— ๋”ฐ๋ผ ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.(์œ ์—ฐํ•œ ๋ณด์•ˆ ์„ค์ •)

 

๋ฉ”์„œ๋“œ ์ธ๊ฐ€๋Š” ๋ฉ”์„œ๋“œ ์ „ํ›„์— ์ธ๊ฐ€๋ฅผ ์ ์šฉํ•˜๋Š” ์กฐํ•ฉ์ด๋‹ค. 

 

๋‹ค์Œ์€ ๋ฉ”์„œ๋“œ ๋ณด์•ˆ์ด ์ ์šฉ๋œ ์˜ˆ์ด๋‹ค. 

@Service
public class MyCustomerService {
    @PreAuthorize("hasAuthority('permission:read')")
    @PostAuthorize("returnObject.owner == authentication.name")
    public Customer readCustomer(String id) { ... }
}

 

 

1. Spring AOP๋Š” readCustomer์— ๋Œ€ํ•œ ํ”„๋ก์‹œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. AuthorizationManagerBeforeInterceptor๊ฐ€ @PreAuthorize ํฌ์ธํŠธ์ปท๊ณผ ์ผ์น˜ํ•œ๋‹ค. 

2. ์ธํ„ฐ์…‰ํ„ฐ๋Š” PreAuthorizeAuthorizationManaer#check์„ ํ˜ธ์ถœํ•œ๋‹ค. 

3. ๊ถŒํ•œ ๊ด€๋ฆฌ์ž๋Š” ์–ด๋…ธํ…Œ์ด์…˜์˜ SpEl ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๊ณ , MethodSecurityExpressionRoot๋ฅผ ํฌํ•จํ•œ MethodSecurityExpressionHandler๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ํ‘œํ˜„์‹์— ๋Œ€ํ•œ EvaluationContext๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. 

4. ์ธํ„ฐ์…‰ํ„ฐ๋Š” 3๋ฒˆ์˜ EvaluationContext๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•œ๋‹ค. ๊ตฌ์ฒด์ ์œผ๋กœ๋Š” ์ธํ„ฐ์…‰ํ„ฐ๋Š” Supplier์—์„œ ์ธ์ฆ ์ •๋ณด๋ฅผ ์ฝ๊ณ , ํ•ด๋‹น ๊ถŒํ•œ ๋ชฉ๋ก์— permission:read ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. 

5. ํ‰๊ฐ€๊ฐ€ ํ†ต๊ฐ€ํ•˜๋ฉด Spring AOP๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. 

6. ํ‰๊ฐ€๊ฐ€ ์‹คํŒจํ•˜๋ฉด ์ธํ„ฐ์…‰ํ„ฐ๋Š” AuthorizationDeniedEvent๋ฅผ ๊ฒŒ์‹œํ•˜๊ณ  AccessDeniedException์„ ๋˜์ง„๋‹ค. ์ด ์˜ˆ์™ธ๋Š” ExceptionTranslationFilter์—์„œ catch ๋˜๊ณ  ์‘๋‹ต์— 403 ์ƒํƒœ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 

7. ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋œ ํ›„์—๋Š” Spring AOP๊ฐ€ @PostAuthorize ํฌ์ธํŠธ์ปท๊ณผ ์ผ์น˜ํ•˜๋Š” AuthorizationManagerAfterMethodInterceptor๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด์ „๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋™์ž‘ํ•˜์ง€๋งŒ PostAuthorizationManager๊ฐ€ ์‚ฌ์šฉ๋œ๋‹ค. 

8. ํ‰๊ฐ€๊ฐ€ ํ†ต๊ณผํ•˜๋ฉด ์ฒ˜๋ฆฌ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๊ณ„์†๋œ๋‹ค. 

9. ํ‰๊ฐ€๊ฐ€ ์‹คํŒจํ•˜๋ฉด ์ธํ„ฐ์…‰ํ„ฐ๋Š” AuthorizationDeniedEvent๋ฅผ ๊ฒŒ์‹œํ•˜๊ณ  AccessDeniedException์„ ๋˜์ง„๋‹ค. ์ด ์˜ˆ์™ธ๋Š” ExceptionTranslationFilter์—์„œ catch๋˜๊ณ  ์‘๋‹ต์— 403 ์ƒํƒœ ์ฝ”๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค. 

 

์œ„์—์„œ ๋ณด์—ฌ์ค€ ๊ฒƒ์ฒ˜๋Ÿผ, ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ฉ”์„œ๋“œ ๋ณด์•ˆ ์–ด๋…ธํ…Œ์ด์…˜์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด ๊ฐ๊ฐ ์ฒ˜๋ฆฌํ•œ๋‹ค. ์ด๋Š” ์ด๋Ÿฌํ•œ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ชจ๋‘ "AND"๋กœ ์—ฐ๊ฒฐ๋œ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค์‹œ๋งํ•ด, ์ธ๊ฐ€๋˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ชจ๋“  ์–ด๋…ธํ…Œ์ด์…˜ ๊ฒ€์‚ฌ๊ฐ€ ํ†ต๊ณผ๋˜์–ด์•ผ ํ•œ๋‹ค. 

 

๊ทธ๋Ÿฌ๋‚˜ ๋™์ผํ•œ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋™์ผํ•œ ๋ฉ”์„œ๋“œ์— ์—ฌ๋Ÿฌ๋ฒˆ ๋ฐ˜๋ณตํ•˜๋Š” ๊ฒƒ์€ ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด, ๋™์ผํ•œ ๋ฉ”์„œ๋“œ์— ๋‘ ๋ฒˆ์˜ @PreAuthorize๋ฅผ ๋ฐฐ์น˜ํ•  ์ˆ˜ ์—†๋‹ค. 

 

๊ฐ๊ฐ์˜ method ์ธ๊ฐ€ ์–ด๋…ธํ…Œ์ด์…˜์€ ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜ ๋˜๋Š” ๋ฉ”ํƒ€ ์–ด๋…ธํ…Œ์ด์…˜ ์ƒ์œ„ ํ•ญ๋ชฉ์„ ์ฐพ๋Š” ๊ณ ์œ ํ•œ ํฌ์ธํŠธ์ปท ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ–๋Š”๋‹ค. ๋˜ํ•œ ๊ฐ๊ฐ์˜ ์–ด๋…ธํ…Œ์ด์…˜์€ ๊ณ ์œ ํ•œ ๋ฉ”์„œ๋“œ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ๊ฐ–๋Š”๋‹ค. ์ด๋Š” ๊ธฐ๋Šฅ์„ ๋”์šฑ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. 

 

๊ฐ ๋ฉ”์„œ๋“œ ์ธํ„ฐ์…‰ํ„ฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. 

 

@PreAuthorize

AuthenticationManagerBeforeMethodInterceptor#preAuthorize

PreAuthorizeAuthorizationManager

@PostAuthorize

AuthenticationManagerAfterMethodInterceptor#postAuthoriza

PostAuthorizaAuthorizationManager

@PreFilter

PreFilterAuthorizationMethodInterceptor

@PostFilter

PostFilterAuthorizationMethodInterceptor

@Secured

AuthenticationManagerBeforeMethodInterceptor#secured

SecuredAuthorizationManager

 

๋˜ํ•œ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณต์žกํ•œ SqEL ํ‘œํ˜„์‹์„ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์ด ์œ ํ˜น์ ์ผ ์ˆ˜ ์žˆ๋‹ค. 

@PreAuthorize("hasAuthority('permission:read') || hasRole('ADMIN')")

 

๊ทธ๋Ÿฌ๋‚˜ ์œ„์˜ ์ฝ”๋“œ ๋Œ€์‹  RoleHierarchy๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ROLE_ADMIN์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž์—๊ฒŒ permissioned:read๋ฅผ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋‹ค. 

@Bean
static RoleHierarchy roleHierarchy() {
    return new RoleHierarchyImpl("ROLE_ADMIN > permission:read");
}

 

๊ทธ๋Ÿฐ ๋‹ค์Œ MethodSecurityExpressionHandler ์ธ์Šคํ„ด์Šค์— ์„ค์ •ํ•˜๋ฉด ๋” ๊ฐ„๋‹จํ•œ @PreAuthorize ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

@PreAuthorize("hasAuthority('permission:read')")

๐ŸŽ Requst-level vs Method-level Authorization

  request-level method-level
authorization type ์ „์ฒด ์—‘์„ธ์Šค ๋ ˆ๋ฒจ ์„ธ๋ถ€์ ์ธ ์—‘์„ธ์Šค ๋ ˆ๋ฒจ
configure location config class ๋‚ด ๋กœ์ปฌ ๋ฉ”์„œ๋“œ ๋‚ด
configuraion style DSL Annotaion
authorization definition programmatic(์ฝ”๋”ฉ) SqEL(Spring Expression Language)

** DSL(Domain Specific Language) : ํŠน์ • ๋„๋ฉ”์ธ(์˜ˆ : Spring Security)์— ํŠนํ™”๋œ ์–ธ์–ด

 

์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ๋ฉ”์„œ๋“œ ๋ณด์•ˆ์„ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์€ ์–ด๋…ธํ…Œ์ด์…˜์ด ์—†๋Š” ๋ฉ”์„œ๋“œ๋Š” ๋ณดํ˜ธ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด HttpSecurity ์ธ์Šคํ„ด์Šค์—์„œ catch-all(๋ชจ๋“  ๊ฒฝ์šฐ๋ฅผ ๋‹ค๋ฃจ๋Š”) ์ธ๊ฐ€ ๊ทœ์น™์„ ์„ ์–ธํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค. ์ด ๊ทœ์น™์€ ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•ด ์ ์šฉ๋˜๋ฉฐ, ์ผ์น˜ํ•˜๋Š” ๋‹ค๋ฅธ ๊ทœ์น™์ด ์—†์„ ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „๋ฐ˜์— ๋ณด์•ˆ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. 


@PreAuthorize

@Component
public class BankService {
	@PreAuthorize("hasRole('ADMIN')")
	public Account readAccount(Long id) {
        // ... is only invoked if the `Authentication` has the `ROLE_ADMIN` authority
	}
}

 

@PostAuthorize

@Component
public class BankService {
	@PostAuthorize("returnObject.owner == authentication.name")
	public Account readAccount(Long id) {
        // ... is only returned if the `Account` belongs to the logged in user
	}
}

 

์ด ๊ฒฝ์šฐ์—๋Š” ์ฃผ์–ด์ง„ ํ‘œํ˜„์‹ returnObject.owner == authentication.name์ด ํ†ต๊ณผํ•  ๊ฒฝ์šฐ์—๋งŒ ๋ฉ”์„œ๋“œ๊ฐ€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ returnObject๋Š” Account๋ฅผ ๋งํ•œ๋‹ค. ์ด ํ‘œํ˜„์‹์€ ์š”์ฒญํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ํ•ด๋‹น ๊ณ„์ •์˜ ์†Œ์œ ์ž์ธ์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค. ๋งŒ์•ฝ ํ‘œํ˜„์‹์ด true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ฉ”์„œ๋“œ๊ฐ€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 

 

@PreFilter

@Component
public class BankService {
	@PreFilter("filterObject.owner == authentication.name")
	public Collection<Account> updateAccounts(Account... accounts) {
        // ... `accounts` will only contain the accounts owned by the logged-in user
        return updated;
	}
}

 

ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์€ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ์ˆ˜ํ–‰๋˜๋ฉฐ, ๊ณ„์ •์ด ์†Œ์œ ์ž์™€ ํ˜„์žฌ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„์ด ๊ฐ™์ง€ ์•Š์€ ๊ฒฝ์šฐ ๊ณ„์ • ๊ฐ’์„ ํ•„ํ„ฐ๋งํ•˜์—ฌ ์ œ์™ธํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. filterObject๋Š” accounts์— ํฌํ•จ๋œ ๊ฐ ๊ณ„์ •์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.  

@PreFilter๋Š” ๋ฐฐ์—ด, ์ปฌ๋ ‰์…˜, ๋งต, ์ŠคํŠธ๋ฆผ์„ ์ง€์›ํ•œ๋‹ค. 

 

@PreFilter("filterObject.owner == authentication.name")
public Collection<Account> updateAccounts(Account[] accounts)

@PreFilter("filterObject.owner == authentication.name")
public Collection<Account> updateAccounts(Collection<Account> accounts)

@PreFilter("filterObject.value.owner == authentication.name")
public Collection<Account> updateAccounts(Map<String, Account> accounts)

@PreFilter("filterObject.owner == authentication.name")
public Collection<Account> updateAccounts(Stream<Account> accounts)

 

@PostFilter

@Component
public class BankService {
	@PostFilter("filterObject.owner == authentication.name")
	public Collection<Account> readAccounts(String... ids) {
        // ... the return value will be filtered to only contain the accounts owned by the logged-in user
        return accounts;
	}
}

 

ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์€ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋œ ํ›„์— ๋ฐ˜ํ™˜๊ฐ’์—์„œ filterObject.owner == authentication.name ํ‘œํ˜„์‹์ด ์‹คํŒจํ•˜๋Š” ๊ฒฝ์šฐ ํ•ด๋‹น ๊ฐ’์„ ํ•„ํ„ฐ๋งํ•˜์—ฌ ์ œ์™ธํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. @PostFilter ๋˜ํ•œ ๋ฐฐ์—ด, ์ปฌ๋ ‰์…˜, ๋งต, ์ŠคํŠธ๋ฆผ์„ ์ง€์›ํ•œ๋‹ค. 

 

@Secured

ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์€ @PreAuthorize์˜ legacy option์ด๋‹ค. 

 

Method Security๋Š” ํด๋ž˜์Šค์™€ ์ธํ„ฐํŽ˜์ด์Šค ์ˆ˜์ค€์—์„œ์˜ ์ธ๊ฐ€๋„ ์ง€์›ํ•œ๋‹ค. 

@Controller
@PreAuthorize("hasAuthority('ROLE_USER')")
public class MyController {
    @GetMapping("/endpoint")
    public String endpoint() { ... }
}

 

ํ˜น์€ ์•„๋ž˜์™€ ๊ฐ™์ด ํด๋ž˜์Šค์™€ ๋ฉ”์„œ๋“œ ๋‹จ์œ„์— ๋ชจ๋‘ ์„ ์–ธ๋˜์–ด ์žˆ์„ ์ˆ˜๋„ ์žˆ๋‹ค. 

@Controller
@PreAuthorize("hasAuthority('ROLE_USER')")
public class MyController {
    @GetMapping("/endpoint")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public String endpoint() { ... }
}

 

๋ฉ”์„œ๋“œ ์ˆ˜์ค€์˜ ์–ด๋…ธํ…Œ์ด์…˜์€ ํด๋ž˜์Šค ์ˆ˜์ค€์˜ ์–ด๋…ธํ…Œ์ด์…˜์„ ์žฌ์ •์˜ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ endpoint()๋Š” "hasAuthority('ROLE_ADMIN')"์˜ ์ธ๊ฐ€๋ฅผ ๋”ฐ๋ผ๊ฐ€๊ฒŒ ๋œ๋‹ค. 


๐ŸŽ Meta Annotaion

Method Security๋Š” ๋ฉ”ํƒ€ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง€์›ํ•œ๋‹ค. ์ด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์ • ์–ด๋…ธํ…Œ์ด์…˜์„ ์ž‘์„ฑํ•˜์—ฌ ๊ฐ€๋…์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. 

 

์˜ˆ๋ฅผ ๋“ค์–ด, @PreAuthorize("hasRole('ADMIN')")์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด @IsAdmin์œผ๋กœ ๊ฐ„์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค. 

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('ADMIN')")
public @interface IsAdmin {}

 

์ƒˆ๋กœ ์ •์˜๋œ @IsAdmin ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

@Component
public class BankService {
    @IsAdmin
    public Account readAccount(Long id) {
        // ... is only returned if the `Account` belongs to the logged in user
    }
}

๐ŸŽ ์ผ๋ถ€ ์–ด๋…ธํ…Œ์ด์…˜๋งŒ ํ™œ์„ฑํ™”

@EnableMethodSecurity์˜ ์‚ฌ์ „ ๊ตฌ์„ฑ์„ ํ•ด์ œํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์ง€์ • ๊ฐ€๋Šฅํ•˜๋‹ค. ๋งŒ์•ฝ @PostAuthorize์™€ ๊ฐ™์€ ํŠน์ • ์–ด๋…ธํ…Œ์ด์…˜๋งŒ ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. 

@Configuration
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	Advisor postAuthorize() {
		return AuthorizationManagerBeforeMethodInterceptor.postAuthorize();
	}
}

 

์œ„์˜ ์ฝ”๋“œ๋Š” MethodSecurity์˜ ์‚ฌ์ „ ๊ตฌ์„ฑ์„ ๋น„ํ™œ์„ฑํ™” ํ•œ ๋‹ค์Œ @PostAuthoriza ์ธํ„ฐ์…‰ํ„ฐ ์ž์ฒด๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•œ๋‹ค. 


๐ŸŽ Java ์ฝ”๋“œ๋กœ Authorization(์ธ๊ฐ€)

Custom Bean

MethodSecurityExpressOperations๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๊ฐ€์ง€๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๋Š” ๋นˆ์„ ์„ ์–ธํ•œ๋‹ค. 

@Component("authz")
public class AuthorizationLogic {
    public boolean decide(MethodSecurityExpressionOperations operations) {
        // ... authorization logic
    }
}

 

bean์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

@Controller
public class MyController {
    @PreAuthorize("@authz.decide(#root)")
    @GetMapping("/endpoint")
    public String endpoint() {
        // ...
    }
}

 

์ด ๋ฐฉ์‹์˜ ์ข‹์€ ์ ์€ ๋ชจ๋“  ๊ถŒํ•œ ๋ถ€์—ฌ(Authorize) ๋กœ์ง์ด ๋ณ„๋„์˜ ํด๋ž˜์Šค์— ์žˆ์–ด ๋…๋ฆฝ์ ์œผ๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ฐ ์ •ํ™•์„ฑ ๊ฒ€์ฆ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. 

 

Custom Authorization Manager ์‚ฌ์šฉ

ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ ์œผ๋กœ ์ธ๊ฐ€๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋‘๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ custom AuthorizationManager๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

์•„๋ž˜์™€ ๊ฐ™์ด authorization manager๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. 

@Component
public class MyAuthorizationManager implements AuthorizationManager<MethodInvocation>, AuthorizationManager<MethodInvocationResult> {
    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
        // ... authorization logic
    }

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocationResult invocation) {
        // ... authorization logic
    }
}

 

๊ทธ๋Ÿฐ ๋‹ค์Œ AuthorizationManager๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋Š” ์‹œ์ ์— ํ•ด๋‹นํ•˜๋Š” ํฌ์ธํŠธ์ปท์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์„œ๋“œ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ๊ฒŒ์‹œํ•œ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด @PreAuthoriza ๋ฐ @PostAuthorize ์ž‘๋™ ๋ฐฉ์‹์„ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค. 

@Configuration
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
    @Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	Advisor preAuthorize(MyAuthorizationManager manager) {
		return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	Advisor postAuthorize(MyAuthorizationManager manager) {
		return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
	}
}

 

Customizing Expression Handling

SpEL ํ‘œํ˜„์‹์ด ์ฒ˜๋ฆฌ๋˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉ์ž ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉ์ž ์ •์˜ MethodSecurityExpressionHandler ๋ฅผ ๋…ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ธฐ๋ณธ MethodSecurityExpressionHandler๋ฅผ ์‚ฌ์šฉ์ž ์ •์˜ํ•˜์—ฌ ๊ธฐ๋ณธ๊ฐ’ ์ด์™ธ์˜ ์‚ฌ์šฉ์ž ์ง€์ • ๊ถŒํ•œ ๋ถ€์—ฌ ํ‘œํ˜„์‹์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค. 

@Bean
static MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
	DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
	handler.setRoleHierarchy(roleHierarchy);
	return handler;
}

 

MethodSecurityExpressionHandler๋Š” Spring Security์—์„œ SpEL ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•˜๊ณ  ๊ถŒํ•œ ๋ถ€์—ฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ๋Š” MethodSecurityExpressionHandler๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” DefaultMethodSecurityExpressionHandler์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 

DefaultMethodSecurityExpressionHandler๋Š” Spring Security์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณต๋˜๋Š” ๋ฉ”์„œ๋“œ ๋ณด์•ˆ ํ‘œํ˜„์‹ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์œ„์˜ ์ฝ”๋“œ์—์„œ๋Š” setRoleHierarchy ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ RoleHierarchy๋ฅผ ์„ค์ •ํ•˜๊ณ  ์žˆ๋‹ค.


๐ŸŽ Method Parameter ์‚ฌ์šฉ

์ถ”๊ฐ€์ ์œผ๋กœ Spring Security๋Š” ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋˜ํ•œ SpEL ํ‘œํ˜„์‹์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. 

DefaultSecurityParameterNameDiscoverer๋Š” ์‚ฌ์šฉํ•˜์—ฌ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ๊ฒ€์ƒ‰ํ•œ๋‹ค. 

import org.springframework.security.access.method.P;

...

@PreAuthorize("hasPermission(#c, 'write')")
public void updateContact(@P("c") Contact contact);

 

Spring Security๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ฒฝ์šฐ @P ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค. 

ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ Contanct๊ฐ€ write ๊ถŒํ•œ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. 

 

import org.springframework.data.repository.query.Param;

...

@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);

 

Spring Data๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ฒฝ์šฐ @Param์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ด๋ฆ„์ด ์ธ์ฆ์˜ ์ด๋ฆ„์ด ๊ฒฝ์šฐ์—๋งŒ ํ˜ธ์ถœ์ด ์ธ๊ฐ€๋จ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. 


๐ŸŽ Domain Object Security(ACLs)

Spring Secuiry๋Š” ์š”์ฒญ๊ณผ ๋ฉ”์„œ๋“œ ์ธ๊ฐ€์— ๋Œ€ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋„๋ฉ”์ธ ๊ฐ์ฒด์˜ ๋ณด์•ˆ ๊ธฐ๋Šฅ๋„ ์ œ๊ณตํ•œ๋‹ค. 

 

๋ณต์žกํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ข…์ข… ์›น ์š”์ฒญ์ด๋‚˜ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์˜ ์ˆ˜์ค€์„ ๋„˜์–ด ์•ก์„ธ์Šค ๊ถŒํ•œ์„ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค. ๋ณด์•ˆ ๊ฒฐ์ •์€ ๋ˆ„๊ฐ€(Authentication), ์–ด๋””์„œ(MehtodInvocation), ๋ฌด์—‡์— ๋Œ€ํ•œ ๊ฒƒ(SomeDomainObject)๋กœ ๊ตฌ์„ฑ๋˜์–ด์•ผ ํ•œ๋‹ค. ๋‹ค์‹œ ๋งํ•ด, ์ธ๊ฐ€ ๊ฒฐ์ •์€ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์˜ ์ฃผ์ฒด๊ฐ€ ๋˜๋Š” ์‹ค์ œ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค. 

 

์˜ˆ๋ฅผ ๋“ค์–ด, ์• ์™„๋™๋ฌผ ํด๋ฆฌ๋‹‰์„ ์œ„ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ค๊ณ„ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž. Spring ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ฃผ์š” ์‚ฌ์šฉ์ž ๊ทธ๋ฃน์€ ์• ์™„๋™๋ฌผ ํด๋ฆฌ๋‹‰์˜ ์ง์›๊ณผ ๊ณ ๊ฐ์ด๋‹ค. ์ง์›์€ ๋ชจ๋“  ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‚˜, ๊ณ ๊ฐ์€ ์ž์‹ ์˜ ๊ณ ๊ฐ ๋ ˆ์ฝ”๋“œ๋งŒ ๋ณผ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ์ข€ ๋” ๋ณต์žกํ•˜๊ฒŒ ํ•˜๊ธฐ์œ„ํ•ด ์ถ”๊ฐ€์ ์œผ๋กœ ๊ณ ๊ฐ์€ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ž์‹ ์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐ•์•„์ง€ ์œ ์น˜์›์˜ ๋‹ด๋‹น ์„ ์ƒ๋‹˜์ด๋‚˜ ๊ฐ•์•„์ง€ ๋™ํ˜ธํšŒ์˜ ํšŒ์žฅ์—๊ฒŒ ์ž์‹ ์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋„๋ก ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค. Spring Security๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

1. ๋น„์ฆˆ๋‹ˆ์Šค ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ๋ณด์•ˆ์„ ๊ฐ•์ œํ•œ๋‹ค.

๋น„์ฆˆ๋‹ˆ์Šค ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ ๋ณด์•ˆ ๊ทœ์น™์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Customer ๋„๋ฉ”์ธ ๊ฐ์ฒด์˜ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์—‘์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

→ ๊ถŒํ•œ ํ™•์ธ์„ ๋น„์ฆˆ๋‹ˆ์Šค ์ฝ”๋“œ์— ๊ฒฐํ•ฉํ•˜์—ฌ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ค์›Œ์ง€๋ฉฐ, Customer ๊ถŒํ•œ ๋กœ์ง์„ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค. 

 

2. AccessDecisionVoter๋ฅผ ์ž‘์„ฑํ•˜์—ฌ  Authentication์— ์ €์žฅ๋œ GrantedAuthority[] ์ธ์Šคํ„ด์Šค์— ๋ณด์•ˆ์„ ๊ฐ•์ œํ•œ๋‹ค.

์ด ๋ฐฉ๋ฒ•์€ Spring Security์˜ AccessDecisionVoter ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ Authentication ๊ฐ์ฒด์— ์ €์žฅ๋œ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ณด์•ˆ์„ ๊ฐ•์ œํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ฐ ๋„๋ฉ”์ธ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ๊ถŒํ•œ์€ ์‚ฌ์šฉ์ž ์ •์˜ GrantedAuthority[] ๊ฐ์ฒด๋กœ Authentication์— ์„ค์ •๋œ๋‹ค. AccessDecisionVoter๋Š” ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์˜ ์ธ๊ฐ€ ๊ฒฐ์ •์„ ๋‚ด๋ฆฐ๋‹ค. 

→ ๋งŒ์•ฝ ํ•˜๋‚˜์˜ ๋™ํ˜ธํšŒ๊ฐ€ 5000๋ช…์˜ ๊ณ ๊ฐ์„ ํ™•๋ณดํ–ˆ๋‹ค๋ฉด, 5000๋ช…์˜ ๊ณ ๊ฐ์— ๋ชจ๋‘ ์ ‘๊ทผํ•˜์—ฌ ๊ถŒํ•œ์„ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. ์ด๋Š” Authentication  ๊ฐ์ฒด๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ์†Œ๋น„๋˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ์–‘๊ณผ ์‹œ๊ฐ„์ด ๋งŽ์•„์ง„๋‹ค๋Š” ๋œป์ด๋‹ค. 

 

3. AccessDecisionVoter๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ๋ณด์•ˆ์„ ๊ฐ•์ œํ•˜๊ณ  ๋Œ€์ƒ ๊ณ ๊ฐ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์—ด์–ด๋ณด๋Š” ๊ฒƒ์ด๋‹ค.

์ด ๋ฐฉ๋ฒ•์€ DAO(Data Access Object)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ€์ƒ ๋ชจ๋ฐ์ธ ๊ฐ์ฒด๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  ํ•ด๋‹น ๊ฐ์ฒด์˜ ์Šน์ธ๋œ ์‚ฌ์šฉ์ž ์ปฌ๋ ‰์…˜์— ์—‘์„ธ์Šคํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ถŒํ•œ์„ ํ™•์ธํ•œ๋‹ค. AccessDecisionVoter๋Š” ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ธ๊ฐ€ ๊ฒฐ์ •์„ ๋‚ด๋ฆฐ๋‹ค.

→ 1, 2๋ฒˆ ๋ฐฉ๋ฒ•์— ๋น„ํ•ด ์ข‹์ง€๋งŒ ๋น„์ฆˆ๋‹ˆ์Šค ๋ฉ”์„œ๋“œ ์ž์ฒด๊ฐ€ Customer ๊ฐ์ฒด๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋Š” DAO์— ๋Œ€ํ•œ ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ๋‹น ๋‘ ๋ฒˆ์˜ ์—‘์„ธ์Šค๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ์ ์—์„œ ๋น„ํšจ์œจ์ ์ด๋‹ค. 

 

์ด๋Ÿฌํ•œ ๋น„ํšจ์œจ๊ณผ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ACL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

์ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜๊ธธ ๋ฐ”๋ž€๋‹ค. 

https://docs.spring.io/spring-security/reference/servlet/authorization/acls.html

 

Domain Object Security (ACLs) :: Spring Security

Complex applications often need to define access permissions beyond a web request or method invocation level. Instead, security decisions need to comprise who (Authentication), where (MethodInvocation), and what (SomeDomainObject). In other words, authoriz

docs.spring.io


๐ŸŽ Authorization Events

๊ฑฐ๋ถ€๋œ ๊ถŒํ•œ์— ๋Œ€ํ•ด์„œ๋Š” AuthorizationDeniedEvent๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋˜ํ•œ ๋ถ€์—ฌ๋œ ๊ถŒํ•œ์— ๋Œ€ํ•ด์„œ๋„ AuthorizationGrantedEvent๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๋‹ค. 

 

์ด๋Ÿฌํ•œ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € AuthorizationEventPublisher๋ฅผ ๊ฒŒ์‹œํ•ด์•ผ ํ•œ๋‹ค. 

@Bean
public AuthorizationEventPublisher authorizationEventPublisher
        (ApplicationEventPublisher applicationEventPublisher) {
    return new SpringAuthorizationEventPublisher(applicationEventPublisher);
}

 

๊ทธ ๋‹ค์Œ Spring์˜ @EventListener๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

@Component
public class AuthenticationEvents {

    @EventListener
    public void onFailure(AuthorizationDeniedEvent failure) {
		// ...
    }
}

 

์ด ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ๊ถŒํ•œ ๊ฑฐ๋ถ€ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•˜์—ฌ ์ ์ ˆํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.