์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
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 |
- ๊ฐ์ดํ ์ข ๋ญ๊ฐ๋น
- css
- ์๋ฐ
- Java
- ๊น๋ฏธ๊ฒฝ์๋งํ์์
- K๋ฐฐํฐ๋ฆฌ๋ ๋ณผ๋ฃจ์
- ์นดํ๋๊ฐ
- ์ฝ๋ฉ
- Python
- ๊ฐ๋ฐ
- ํฐ์คํ ๋ฆฌ์ฑ๋ฆฐ์ง
- ์๋ฐ์คํฌ๋ฆฝํธ
- ํ์ด์ฌ
- ์ฑ
- JavaScript
- ํ์ฒ์ ๋ฆฌํธ๋ฆฌํธ
- ์ํ์ฃผ
- database
- ์ ๋ฆฌํธ๋ฆฌํธ
- ๋๊ฐ
- ์ํ
- ํ๋ก๊ทธ๋๋ฐ
- ๋ฐ์ํ
- ์นํผ๋ธ๋ฆฌ์ฑ
- ์ค๋ผํด
- ๋ฐ์ดํฐ๋ฒ ์ด์ค
- ์ค๋ธ์
- ๋ ์
- ๋ฐฐ์์ ๋ฐฐ์
- html
- Today
- Total
JiYoung Dev ๐ฅ
Spring Security ์ ์ฉ๊ธฐ (7) Spring Security Authorization(Method Security) ๋ณธ๋ฌธ
Spring Security ์ ์ฉ๊ธฐ (7) Spring Security Authorization(Method Security)
Shinjio 2024. 5. 6. 21:09์ง๋๋ฒ์ ์ด์ด ์ด๋ฒ์๋ Authorization์ Method Security์ ์ถ๊ฐ ๋ด์ฉ์ ๋ํ ๋ด์ฉ์ ์ ๋ฆฌํ๋ค.
Spring Security ๊ณต์ ๋ฌธ์
https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html
๐ 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
๐ 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) {
// ...
}
}
์ด ๋ฐฉ๋ฒ์ ํตํด ๊ถํ ๊ฑฐ๋ถ ์ด๋ฒคํธ๋ฅผ ์บ์นํ์ฌ ์ ์ ํ ์์ ์ ์ํํ ์ ์๋ค.
'Study > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
JDBC์ Spring JDBC ๊ธฐ๋ณธ ์ดํดํ๊ธฐ (1) | 2024.11.19 |
---|---|
Base64 Encoding vs Base64UrlSafe vs FormData (1) | 2024.11.05 |
Spring Security ์ ์ฉ๊ธฐ (6) Spring Security Authorization(HttpServletRequests) (0) | 2024.05.05 |
Spring Security ์ ์ฉ๊ธฐ (5) Spring Security Authentication ๋์์๋ฆฌ (0) | 2024.05.05 |
Spring Security ์ ์ฉ๊ธฐ (3) JWT(JSON Web Tokens) ๊ฐ๋ (0) | 2024.04.29 |