FilterSecurityInterceptor
는 Default로 WebExpressionVoter, Method에서는 Default로 RoleVoter, AuthenticatedVoter, PIAAVoter 사용
RoleVoter, AuthenticatedVoter는 올드하다
Role 기반의 권한은 리눅스부터 아파치, 톰켓등 IT 초기부터 전통적으로 구현해서 사용하던 가장 직관적인 권한 체계
-> 하지만, Role 을 기반으로 권한을 판단하기엔, 상황이 너무 다양
-> Role 을 확장한 Authority 기반의 권한 체계
를 사용하고 있다
-> 그렇지만, 기존의 Role 기반이 가지고 있는 직관적이고 계층적인 사용성을 그대로 사용할 수 있도록 해주기 위해 RoleVoter를 사용하기도 함
인증(통행증)을 받았다면 그 인증의 종류가 어떤 종류인지를 판단
이제 막 인증
을 받고 들어온 사용자 VS RememberMe 토큰
을 통해서 들어온 사용자 VS 익명 사용자
를 구분하기 위해 사용
Remember me는 fully authenticated로 보진 않음
-> Fully Authenticated는 토큰 만으론 믿을 수 없다!, 재 로그인하라는 의미
표현식을 통해 객체의 값을 가져오거나 동작을 시켜주는 기능
사실상 컴파일언어라고 할 수 있는 자바를 스크립트언어처럼 동작하게 해주는 기능
ex) 웹에서 타이핑 해서 스프링 빈이나 비즈니스에 반영되도록 하기
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
class Person{
private String name;
private int height;
public boolean over(int pivot){
return height >= pivot;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
class Horse{
private String name;
private int height;
public boolean over(int pivot){
return height >= pivot;
}
}
public class SpELTest {
ExpressionParser parser = new SpelExpressionParser();
Person person = Person.builder().name("홍길동").height(178).build();
Horse nancy = Horse.builder().name("nancy")
.height(160).build();
@Test
void test_1(){
assertEquals("홍길동", parser.parseExpression("name")
.getValue(person));
}
@Test
void test_2(){
//이러한 방식으로 빈에 있는 값을 스크립트로 수정이 가능함
parser.parseExpression("name").setValue(person, "호나우두");
assertEquals("호나우두", parser.parseExpression("name")
.getValue(person, String.class));
}
@Test
void test_3(){
// 빈에 있는 객체의 메서드 호출 테스트
assertTrue(parser.parseExpression("over(170)").getValue(person
, Boolean.class));
assertFalse(parser.parseExpression("over(170)").getValue(nancy
, Boolean.class));
}
@Test
void beanTest(){
// Context 테스트
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.setBeanResolver(new BeanResolver() {
// BeanResolver : 빈 네임으로 빈을 찾아 주는 것
@Override
public Object resolve(EvaluationContext context, String beanName) throws AccessException {
return beanName.equals("person") ? person : nancy;
}
});
// 컨텍스트는 빈만 가져오는게 아니라 루트 객체 설정 가능
ctx.setRootObject(person);
// 밑에 코드와 동작 같음
assertTrue(parser.parseExpression("over(170)").getValue(ctx
, Boolean.class));
// person이라는 빈에서 이 사람이 170이 넘는지 보는 것
assertTrue(parser.parseExpression("@person.over(170)").getValue(ctx
, Boolean.class));
assertFalse(parser.parseExpression("@nancy.over(170)").getValue(ctx
, Boolean.class));
ctx.setVariable("horse", nancy); // 변수 접근 가능
assertFalse(parser.parseExpression("#horse.over(170)").getValue(ctx
, Boolean.class));
}
}
프로퍼티, 메서드, 필드를 처리하고 타입변환을 수행하는 표현식을 평가할 때 EvaluationContext
인터페이스를 사용하는데 이 구현체 중 하나인 StandardEvaluationContext
는 객체를 조작하려고 리플렉션을 사용하고 성능을 향상시키기 위해서 java.lang.reflect의 Method, Field, Constructor를 캐싱
RoleVoter 는 SpEL을 사용하는 WebExpressionVoter
나 PIAAVoter
(PreInvocationAuthorizationAdviceVoter)
가 사용성을 대체하는 추세
스프링 Security 에서 SpEL을 사용해 권한 검증을 하나의 Voter로 통일하기 시작
-> 의미있게 남은 Voter 는 WebExpressionVoter
와 PIAAVoter
밖에 남지 않았다고 볼 수 있다
WebExpressionVoter와 PreInvocationAuthorizationAdviceVoter가 SPEL
사용
WebExpressionVoter는 WebSecurityExpressionRoot
를 Context의 루트로 보고 있음
-> Bean resolve는 Application Context가 될 것
PIAA voter는 MethodSecurityExpressionRoot
를 컨텍스트의 루트로 봄
각각은 SecurityExpressionRoot
라는 공통객체를 상속받아 구현 하고 있음
Role
로 판단을 할지 Authority
로 판단을 할지 명확하게 구분isAnonymous
, isAuthenticated
, isFullyAuthenticated
: AuthenticatedVoter 의 기능을 대체hasPermission
기존의 AuthenticatedVoter나 Roler Voter는 Authentication Token
을 검사하였음
그런데 권한 검사를 제대로 하려면 , 통행증만 아니라 통행증으로 가져가려고 하는 물건이 뭔지도 검사를 해야 함
-> hasPermission
이해를 위해 기말고사 시험지를 작성중이라 가정 해 보자
이러한 케이스는 AuthenticatedVoter나 Roler Voter로 다룰수 없다
이런 경우라면 당연히 어떤 시험지인지, 그리고 그 시험지에 대한 열람 권한이 누구에게 있는지, 즉 여러가지 데이터들이 모여야 해당 권한을 판단할 수가 있다
-> 이런 경우에는 PermissionEvaluator
를 사용하거나 객체 별로 접근 권한을 DB로 관리(권한 테이블) 해주는 ACL
처럼 권한을 체크하기 위한 별도의 설계가 들어가야 합
ebExpressionVoter와 PIIAVoter 는 모두 아래와 같은 방식으로 SpEL 을 사용
WebExpressionVoter, PIAA Voter는 SPEL을 사용해야 하기에, parser가 Context를 가지고 핸들링 할 수 있어야 함
-> parser가 Context를 핸들링기에 각 Voter들은 ExpressionHandler
객체를 가지고 있어야 함
루트 객체
또한 필요하기에 createEvaluationContext, createSecurityExpressionRoot가 필요
web에는 hasipAddress
라는 메서드가 추가로 제공되는데 이는 루트 객체이기에 #
붙일 필요 X
Method 에는 filterObject, return Object에 #을 붙이기 않고도 접근 가능
웹에서 만약 url에 PathVariable을 넘기면 , 이때 PathVariable은 컨텍스트에서 Variable로 취급하기에 #을 붙여야 함
Bean을 가지고 검사한다면 @를 붙여 빈을 가져와 해당 메서드를 검사하는 작업을 수행해야 한다