본문 바로가기

웹 프로그래밍/스프링

스프링 시큐리티에서 뷰(View)에 대한 접근 설정

스프링 시큐리티 뷰(View)에 대한 접근 설정방법

 

이번 멋쟁이사자처럼 최종 프로젝트를 하면서 jwt 토큰을 이용해서 스프링 시큐리티 설정의 통해 REST Api 접근을 통제하는것이 가능했다.

일단 뷰는 모든 사용자가 접근하고 그후에 어떤 Get 요청이라든지 Post 요청이라든지는 아래와 같이 시큐리티의

requestMatchers를 설정하여 권한 설정이 가능했다.

requestMatchers(HttpMethod.GET, "/something").hasRole("ADMIN")

 

이렇게 모든것이 잘풀린다고 생각했던 찰나 그럼 뷰는 어떻게 막는지에 대한 의문이 생겼다.

왜 이런 의문이 생겼나면 어드민만의 뷰가 존재했기 때문이다. 즉 다른 사용자는 사용할수 없는 페이지가 있는데 이곳은 어드민만 보이는 버튼을 통해서 진입이 가능하다. 하지만 어떤 사용자가 해당 URL을 주소창에 직접 치고 들어오는 순간 이것을 막을 방법이 없어진다. 

단순히 해당 URL을 requestMatchers를 설정하면 되는것 아닌가라고 생각할수 있지만 그러면 어드민 사용자도 막혀버리는 일이 생기고 만다. 문제의 원인은 해당 URL로 접근할때 javascript가 실행되지 않으면서 헤더에 Bearer token 설정을 할수 없는것이 문제였다.

 

첫번째 솔루션

그래서 첫번째로 생각한 방법이 아 그럼 View를 위한 View의View 컨트롤러를 따로 만들어 두고 기존의 View 컨트롤러 URL를 시큐리티 설정으로 어드민만 가능하도록 막고 View의 View 컨트롤러의 URL은 permitAll()해서 모두가 접근할수 있게 하는 방법이다. 도식도로 나타내면 아래와 같다.

 

View의 View는 단순이 javascript를 뿌려 주도록 해서 localStorage에 들어있는 토큰을 꺼내어 헤더에 Bearer token 설정을 함으로써 내가 누구인지 증명하는 방법이다. 이렇게 이론적으로 오류가 없다고 생각했는데 구현에 문제가 생겼다.

 

바로 javascript에서 헤더 설정으로 하고 리다이렉트를 하면 전의 헤더 정보나 바디정보가 남아있지 않게 되고 새로운 Get 요청으로 바뀐다는 사실을 알게되었다. 따라서 쿠키를 쓸수밖에 없었고 javascript에서 직접 localStorage에 있는 jwt 토큰을 꺼내서 쿠키에 담고 리다이렉트하는 방법으로 구현이 가능했고 이 방법으로 View를 막는데 성공하였다.

하지만 지금까지 Bearer token 방식을 쓰다가(Oauth2 인증 제외) 여기서는 단순히 View를 막기위해서 쿠키방식을 써야하는것이 통일성이 있지 않다고 느꼈고 다른 방법은 없는지 찾기 시작했다.

 

두번째 솔루션

그래서 두번째 방법을 알게 되었고 이 방법을 중점적으로 설명하고자 한다.

이번에도 View의 View 컨트롤러와 비슷한 역할을 하는 또다른 컨트롤러가 필요하다. 이 컨트롤러는 일반 컨트롤러는 아니고 Rest 컨트롤러이다. 이 컨트롤러의 용도는 도식도로 먼저 나타내자면 아래와 같다.

  1. 일단 모든뷰(관리자뷰 포함)는 모든 사용자가 접근이 가능하다.
  2. 뷰의 URL에 접근하는순간 html는 로드 된다. 
  3. 이와 동시에 jQuery에서 제공하는 기능인 $(document).ready(function() {}) 실행된다. 즉 문서가 완전히 로드되고 준비되었을 때 지정한 함수를 실행하게 할수 있다.
  4. 가장 먼저 실행하는 요청은 바로 위 그림에서와 같이 RestViewController에 토큰을 전달해서 내가 누구인지 증명하는 것이다.
  5. 모든 뷰가 permitAll()로 접근이 가능한것처럼 보이지만 사실은 간접적으로 접근제어를 하는것이다!
package com.example.stagealarm.user.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class RestViewController {
    @GetMapping("/admin")
    public void admin(){

    }

    @GetMapping("/authenticated")
    public void authentication(){

    }
}

 

굉장히 단순한 컨트롤러지만 이 컨트롤러의 URL의 "/admin"은 시큐리티에 의해 어드만 사용자만 가능하고

"/authenticated"는 로그인한 사용자만 가능하다.

그리고 뷰의 javascript는 다음과 같이 작성할수 있다.

 

$(document).ready(function() {


        // 처음 html 시작하자마자 RestViewController 에 권한을 확인하는 부분이 추가

        $.ajax({
            type: 'GET',
            url: '/admin', 
            success: function(response) {
                console.log('Permission granted');
                // 성공하면 그대로 진행시켜서 기존 로직이 흘러가게 한다.
            },
            error: function(xhr, status, error) {
                console.error('Permission denied:', xhr.responseText);
                alert('권한이 없습니다.');
                // 그림에서 3. reject 됐을때 바로 뒤로가는 부분
                location.href = '/' // 알림창을 띄우고 메인페이지로 이동
            }
        });

        // 이뒤부터는 기존 로직이 그대로다
 }

 

이렇게 권한이 필요한 뷰의 javascript의 앞부분에 RestViewController에 요청을 보내는 코드를 추가함으로써 더 진행시키고 말지를 결정할수가 있다. 이렇게해서 뷰또한 접근제어 하는데 성공하였다.

이방법이 첫번째 방법보다 기존로직을 수정할 필요없이 앞에 Ajax요청만 추가 해주면 되었기 때문에 더 간단하고 기존의 Bearer 토큰 방식을 그대로 쓰기때문에 통일성을 유지할수 있어서 더 좋은방법이라고 생각이 되었다.

 

느낀점

스프링 시큐리티를 다루면서 보안에 대한 관심이 높아졌고 어떻게하면 더 안전하게 토큰을 관리하면서 접근도 제한을 잘할수 있을까 고민을 많이 했었다.

어떻게보면 단순해보이는 헤더에 토큰을 넣어주는 문제는 프론트와 백엔드를 분리하게 되면서 어떻게 해야하는지 굉장히 신경을 많이 써야하는 까다로운 부분이였고 또 이문제는 보안과 직결되기 때문에 더 주의해야함을 느꼈다. 

'웹 프로그래밍 > 스프링' 카테고리의 다른 글

Docker  (0) 2024.04.12
스프링 시큐리티에서 Redis로 jwt 액세스 토큰, 리프레쉬 토큰 구현  (2) 2024.04.12
OAuth2  (0) 2024.02.14
JWT  (0) 2024.02.13
REST  (0) 2024.01.18