Spring REST API 엔드포인트 권한(인가) 처리
참고 URL
- https://victorydntmd.tistory.com/177
상황
- Spring Interceptor를 이용해서 URI 접근제어 가능함
- 또한 Session 유무를 처리함( Login 처리)
- 근데, 문제는 /portfolio 라는 URI에 대해 GET은 로그인 사용자는 봐도 되나, POST/PUT/DELETE는 권한이 없어야함
- 별의별 접근제어 방법론을 떠오르다, 어떤 블로그 확인함
문제 해결
- Custom Annotation을 이용한 접근제어(정말 간단하게 문제가 해결됨)
문제 해결 과정
- 내가 갖고 있는 타입은 비로그인 유저 / 로그인 유저임
1. Auth라는 Annotation을 생성한다.
Auth.class
@Retention( RetentionPolicy.RUNTIME )
@Target(ElementType.METHOD)
public @interface Auth {
public AuthState authState() default AuthState.USER;
public enum AuthState {
ADMIN( 0 ),
USER( 1 );
private int authState;
AuthState( int arg ) {
this.authState = arg;
}
public int getAuthState() {
return this.authState;
}
}
}
- enum을 사용했다. Java의 enum은 강력하다.
- @Retention - runtime: 해당 정보를 Runtime에서 사용하겠음
- @Target - Method: Class의 Method에만 사용가능함
2. Controller에 Auth 어노테이션 삽입
PortfolioController.class 일부
@GetMapping("")
@Auth
public <T>T listOrPage(HttpServletRequest request, HttpServletResponse response ) throws IllegalArgumentException, IllegalAccessException {
// .. 구현체
}
@Auth(authState = Auth.AuthState.ADMIN)
@DeleteMapping("/{seqId}")
public ResponseEntity<?> deletePortfolioOne( @PathVariable("seqId") int seqId, HttpServletRequest request, HttpServletResponse response ) {
// .. 구현체
}
listOrPage 메소드
- 비로그인 유저가 접근 가능
- Auth의 기본값은 AuthState.USER 이기 때문에 생략 가능
deletePortfolioOne
- 로그인 유저만 접근 가능
3. SessionInterceptor 에 분기처리 추가
SessionInterceptor.class 일부
if( this.hasSession( session ) ) {
// .. Session이 있을 경우의 로직(구현체)
} else {
if ( isAuthorized( 1/*user*/ , handler ) ) {
return true;
}
this.sendRedirect( response, "잘못된 접근 : 로그인하세요." );
return false;
}
private boolean isAuthorized( int type, Object handler ) {
if ( type == 0 ) return true;
else {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Auth auth = handlerMethod.getMethodAnnotation(Auth.class);
if (auth.authState().getAuthState() == type) return true;
else return false;
}
}
- Session이 없으면 로그인 한것이 아님.
- USER type은 1임(위의 if( type == 0 ) return true; 없어도 됨)
- handler를 이용하여 메소드에 연결된 Annotation 값을 추출함
- 추출된 Annotation의 값과 현재 타입을 비교해서 같으면 비로그인 유저도 확인 가능한 거임
끝
4. 응용
- 앤드포인트에 여러 타입의 권한이 혼용될 때
- 비트마스크를 사용한다.
- 굳이 응용하자면 아래와 같다..(Annotation과 Enum Array를 잘 쓰고 싶었는데 생각처럼 되지 않았다. 그냥 참고만해라 더러운 코드다;)
// Auth.class
@Retention( RetentionPolicy.RUNTIME )
@Target(ElementType.METHOD)
public @interface Auth {
public AuthState[] authState() default AuthState.ADMIN;
public enum AuthState {
SUPER_ADMIN( 1 ),
ADMIN( 2 ),
DEVEL_USER( 4 ),
COMMON_USER( 8 ),
NON_USER( 16 );
private int authState;
AuthState( int arg ) {
this.authState = arg;
}
public int getAuthState() {
return this.authState;
}
}
}
// Test.class
public class Sample001 {
@Test
@Auth( authState = {Auth.AuthState.ADMIN, Auth.AuthState.SUPER_ADMIN, Auth.AuthState.DEVEL_USER})
public void test001() throws NoSuchMethodException {
Method method = Sample001.class.getDeclaredMethod( "test001" );
int myBitMask = Auth.AuthState.ADMIN.getAuthState();
Auth auth = method.getAnnotation( Auth.class );
System.out.println( isAuthorized( myBitMask, auth ) );
}
private boolean isAuthorized( int requestedBitMask, Auth auth ) {
int bitMask = 0;
for ( Auth.AuthState enumAuth : auth.authState() ) {
bitMask = bitMask + enumAuth.getAuthState();
}
if ( ( requestedBitMask & bitMask ) == requestedBitMask ) return true;
return false;
}
}
- 되긴됨 ㅋㅋㅋㅋㅋ;
'개발관련 > 삽질' 카테고리의 다른 글
AJP 프로토콜 모든 것을 분석 해보자 (1) | 2019.10.28 |
---|---|
Insert Multiple - Oracle(Tibero) 포팅 삽질기 (0) | 2019.09.11 |
JWT를 분석하고 사용해보자 (3/3) (0) | 2019.08.13 |
JWT를 분석하고 사용해보자 (2/3) (0) | 2019.08.13 |
JWT를 분석하고 사용해보자 (1/3) (0) | 2019.08.13 |