본문으로 바로가기

스프링 시큐리티에서는 권한에 따라 접근 가능한 경로를 제한할 수 있다. 그럼 권한에 따라 다른 링크를 보여준다면, 접근 가능한 경로를 제한할 수 있는 것 아닌가? 라는 생각을 할 수도 있다. 맞다. 눈에 보이는 링크만 클릭할 수 있으니 접근 가능한 경로를 제한할 수 있다. 하지만, 만약에 그 사람이 자기 권한이 아닌 페이지의 경로 주소를 우연히 알게 되었다고 하면, 권한이 있는 것도 아닌데 마음대로 들락날락 거리는 큰 문제가 생긴다. 스프링 시큐리티는 경로 주소에서부터 접근을 제한한다. 따라서 권한을 가지지 않으면 경로 주소를 알고 있더라도 들어가지 못한다. 예를 들어 "/admin/" 이 들어가는 경로들은 전부 관리지만 들어갈 수 있게 한다면, 일반 유저는 아무리 주소창에 경로 주소를 치더라도 들어갈 수 없다. 경로에 따라 접근 권한을 만들어 주는 것이다.

 

권한을 비회원(GUEST), 준회원(USER), 정회원(MEMBER), 관리자(ADMIN) 4가지로 나눴다. 비회원은 그 누구든 접근할 수 있고, 준회원은 로그인을 한 이용자, 정회원은 로그인 한 이용자 중에 정회원 권한을 가진 이용자, 관리자는 로그인 한 이용자 중에 관리자 권한을 가진 이용자만 접근할 수 있다. 이제 경로에 따라 권한 설정을 해보자.

 

기초 셋팅을 할 때 경로에 따라 접근 권한을 설정한 적이 있다. 기초 셋팅때에는 index 페이지에 들어오려면 로그인을 해야 했었다. 모든 경로에 로그인을 해야 한다는 설정을 했기 때문이다. 이제는 index 페이지에는 누구든 들어올 수 있고 권한에 따라 들어갈 수 있는 페이지를 제한할 것이다.

 

1
2
3
4
5
6
<http auto-config="true" use-expressions="true">
    <intercept-url pattern="/member/**" access="hasAnyRole('ROLE_MEMBER','ROLE_ADMIN')"/>
    <intercept-url pattern="/user/**" access="hasAnyRole('ROLE_USER','ROLE_MEMBER','ROLE_ADMIN')"/>
    <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/>
    <intercept-url pattern="/**" access="permitAll"/>
</http>
cs

 

<intercept-url> 태그를 이용하여 pattern 속성의 url 에 access 속성의 권한을 설정했다. /member/ 경로는 정회원, 관리자가 접근할 수 있고,  /user/ 경로는 준회원, 정회원, 관리자가 접근할 수 있고, /admin/ 경로는 관리자만 접근할 수 있다. 그리고 모든 경로(/**)는 로그인 하지 않은 이용자도 접근할 수 있다.

 

<intercept-url> 태그를 사용할 때 반드시 주의해야할 점이 있다. 경로 설정을 하게 되면 태그를 하나가 아닌 여러개를 설정하게 된다. 여러개가 설정할 경우, 설정된 순서대로 위에서부터 매칭되어진다. 따라서 태그의 설정 순서가 중요하다. 태그의 순서로 인해서 꼬여버리거나 무한 루프가 돌 수 있기 때문이다. 가장 특수한 경우를 위에 놓고 아래쪽으로 갈 수록 일반적인 경우를 둬야 한다. 즉, 구체적인 패턴을 먼저 설정해야 한다. /admin/** 패턴과 /** 패턴를 보면, /admin/** 패턴은 admin 디렉토리 밑에 있는 모든 디렉토리와 파일을 의미하고, /** 패턴은 모든 디렉토리와 파일을 의미한다. 즉, /admin/** 은 /**보다 더 구체적인 패턴이라는 뜻이다. 그럼 /admin/** 패턴을 /** 보다 위에 설정해주어야 한다. 만약 /** 패턴을 먼저 설정해버리면 모든 url이 /** 패턴을 만족하게 되고, admin 하위 디렉토리를 가르켜도 /** 패턴을 먼저 매칭시켰기 때문에 admin이 아닌 권한들도 admin 디렉토리에 접근이 가능해져 버린다. 따라서 구체적인 패턴이 먼저 설정되어야 한다. 직접 보고 싶다면 위의 패턴에서 /** 패턴을 제일 위로 올려보자. 그러면 어떤 권한이든 모든 페이지에 접근이 가능해진다.

 

<intercept-url> 태그의 access 속성에 SpEL 문법을 사용할 수 있다. 그러기 위해선 use-expressions 을 true로 해주면 된다. 그리고!! 스프링 시큐리티 4 부터는 use-expressions 속성의 기본값이 true로 변경되었다고 한다. 따라서 use-expressions 속성을 생략해줘도 된다. 근데 왜 굳이 SpEL 문법을 사용하는 것일까? index 페이지에는 모두 접근이 가능하도록 해둔다면, 인증을 받아(로그인을 해서) 권한을 가지고 있는 이용자와 인증을 받지 안혹 권한을 가지고 있지 않는 이용자 모두 접근이 가능해야 한다. SpEL 문법을 사용하지 않는다면, 이러한 경우 모두를 써야한다. 10개의 권한을 나누면, index에 10개 다 적어야 한다. 권한의 추가, 삭제가 있을 때도 마찬가지이다. SpEL 문법을 사용하게 되면 더 쉽고 간단하게 권한 설정을 할 수 있다. 아래의 문법을 참고하여 원하는 표현식을 사용하자.

 

*SpEL 문법

 

 

 

 표현식 설명 
 hasRole('role1')  권한(role1)을 가지고 있는 경우
 hasAnyRole('role1', 'role2')  권한들(role1, role2) 하나라도 가지고 있을 경우 (갯수는 제한없다)
 pemitAll  권한 있든 말든 모두 접근 가능하다.
 denyAll  권한 있든 말든 모두 접근 불가능하다.
 isAnonymous()  Anonymous 사용자일 경우 (인증을 하지 않은 사용자)
 isRememberMe()  Remember-me 기능으로 로그인한 사용자일 경우
 isAuthenticated()  Anonymous 사용자가 아닐 경우 (인증을 한 사용자)
 isFullyAuthenticated()  Anonymous 사용자가 아니고 Remember-me 기능으로 로그인 하지 않은 사용자 일 경우

 

이용자에 따라 권한과 비밀번호를 설정하자. DB 연결을 하지 않았기때문에 태그로 이용자 권한과 정보를 저장한다. user, member, admin 이용자를 만들고 각각의 권한을 부여했다. 그러면 이용자의 권한에 따라 접근할 수 있는 경로가 정해진다.

 

1
2
3
4
5
6
7
8
9
<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user" password="userPw" authorities="ROLE_USER"/>
            <user name="member" password="memPw" authorities="ROLE_MEMBER"/>
            <user name="admin" password="admin" authorities="ROLE_ADMIN"/>
        </user-service>
    </authentication-provider>
</authentication-manager>
cs

 

여기까지 설정이 끝났다면, index 페이지에 버튼을 만들어서 권한에 따라 접근할 수 있는 지 없는 지 확인해야 한다. index페이지의 버튼과 마찬가지로 해당 버튼을 눌렀을 때의 페이지들도 만들었다. 페이지 이동하는 건 다들 할 수 있을 거라 믿어 의심치 않는다. (Controller를 이용,,,) 모르겠다면 마지막에 GitHub 에서 전체코드를 확인하면 된다.

 

1
2
3
4
<a href='<c:url value="/page"/>'>GUEST</a>
<a href='<c:url value="/user/page"/>'>USER</a>
<a href='<c:url value="/member/page"/>'>MEMBER</a>
<a href='<c:url value="/admin/page"/>'>ADMIN</a>
cs

 

페이지까지 만들었다면 실행을 해서 권한에 맞는 경로에 접근할 수 있는 지 확인해보자. 참고로 로그아웃 기능을 아직 만들지 않았기 때문에 한번 로그인을 하면 톰캣을 종료하고 다시 실행을 하던지, 뒤로가기 버튼을 눌러서 로그인 화면이 나왔을 때 다른 권한이 있는 이용자로 로그인해야 한다. 로그아웃 기능은 따로 만들 것이니 일단 경로에 맞게 되는 지 확인부터 하자.

 

 

 

실행을 하면 index페이지가 뜬다. index페이지는 로그인을 하지 않아도 모든 이용자가 접근할 수 있는 페이지이다. GUEST 버튼 또한 비로그인 이용자도 들어갈 수 있다. 하지만 나머지의 버튼들은 비로그인 상태에서 누른다면 로그인 창이 뜰 것이다. 그럼 member 버튼을 눌러서 로그인창이 뜨면 member 권한을 가진 이용자로 로그인을 해보자.

 

 

member 권한을 가진 이용자가 들어갈 수 있는 member 페이지에 들어왔다. member 권한이 있는 이용자는 guest, user, member 페이지에 접근 가능하다. 그럼 member 권한이 있는 이용자인 상태에서 index 페이지에 있는 admin 권한만 접근 할 수 있는 버튼을 눌러보자. 

 

 

 

 

접근 불가능을 알려주는 403 에러 페이지가 떴다. 권한이 없기때문에 들어갈 수 없다는 뜻이다. 마찬가지로 user 권한을 가진 이용자로 로그인하여 테스트를 해보면 guest 와 user 페이지만 접근할 수 있고 member와 admin 페이지에는 접근할 수 없을 것이다. 간단하게 권한에 따라 접근할 수 있는 경로를 제한할 수 있다.

 

권한에 따라 접근 가능한 경로를 설정을 완료했다. 어렵지 않다. 유의해야할 것만 유의해주면 권한 설정은 쉽게 할 수 있다. 권한 설정을 하고 나면, 스프링에서 제공해주는 기본 로그인화면을 커스터마이징 할 차례이다. 화려한 페이지들에 비해 볼품없는 로그인 화면을 원하는 스타일로 커스텀하고, 스프링 시큐리티가 작동하도록 규정에 맞게 설정해주면 된다.

 

GitHub


댓글을 달아 주세요

  1. GGyo245 2020.02.12 20:19

    감사합니다

  2. 게스트 2020.12.25 03:39

    직접 보고 싶다면 위의 패턴에서 /member/** 패턴과 /user/** 패턴의 위치를 바꿔서 실행시키면 알 수 있다. user 권한이 member 디렉토리에 접근하는 것을 확인할 수 있다.

    ↑위와 같은 문구대로 테스트를 해봤는데
    user권한을 가진 걸로 로그인 해보면 member 페이지 접근 안되는데요??

    • BlogIcon #에게 2021.10.29 16:36 신고

      경로가 다를 경우의 순서가 중요하다고 설명한 건데, 예는 경로가 같은 걸로 들어버렸군요^^;
      /** 패턴을 맨 위에 두면, 아래의 접근 권한 설정은 무의미해집니다.