평범한 연구소

[Spring] 시큐리티 Security 본문

Spring Framework

[Spring] 시큐리티 Security

soyeonisgood 2022. 12. 3. 16:29

Spring Security

 - 스프링 시큐리티는 Spring(and Spring Boot로 빌드된 애플리케이션) 웹 애플리케이션과 REST API를 보호하기 위해 널리 사용되는 프레임워크.

 - REST API의 인증과 권한 부여를 제공. DB와 LDAP를 포함해 사용자 자격 증명을 위한 여러 소스와 잘 통합됨.

 - 시큐리티는 애플리케이션을 구축할 때 중요한 비함수적 기능 중 하나.

 

Spring Security 요청 처리 절차

- 주로 서블릿 필터와 이들로 구성된 필터체인으로의 위임 모델을 사용한다. 서블릿 필터는 사용자의 요청을 가로채서 전 처리 하거나 서버의 응답을 가로채서 후처리를 할 수 있다.

 - @MVC의 DIspatcherServlet이나 AOP를 이용해 프록시를 생성하지 않고 DelegatingFilterProxy 클래스를 사용한다. DelegatingFilterProxy를 이용하면서도 AOP 포인트 컷의 활용도 가능하다. DelegatingFilterProxy는 스프링 시큐리티에 정의된 필터로 HTTP 요청 필터링을 위임받아 처리한다.

 - 스프링 시큐리티에서는 threadlocal 이라는 방식으로 사용자 정보를 저장한다. 스레드 로컬도 세션에서 보관 하지만 세션뿐만 아니라 다른 저장소를 이용할 수 있다. 

 

REST API 시큐리티

  • API 사용자는 누구일까?
  • 사용자를 어떻게 식별할까 ?
  • 사용자의 세부 정보를 어디에 저장할 수 있을까?
  • 다른 종류의 사용자는 ?
  • 각 유형의 사용자가 수행할 수 있는 작업은 무엇일까?

 

인증 (Authentication)

 - 일반적으로 사용자 ID와 시크릿 번호를 사용해 사용자를 식별하는데, 이를 사용자 자격 증명이라고 한다.

 - 사용자 자격 증명을 확인하고 API에 유효한 사용자인지 확인하는 과정이 인증이다.

  • 유효한 사용자일까 ?

인증 (Authentication)의 종류

- 크리덴셜(Credential: 자격) 기반 인증

  • 웹에서 사용하는 대부분의 인증 방식은 크리덴셜 기반 인증 방식이다. 즉, 권한을 부여받는데 한 차례의 인증 과정이 필요하며 주로 사용자명과 비밀번호를 입력 받아 저장된 비밀번호와 일치하는지 확인한다.
  • 스프링 시큐리티에서는 아이디를 principle, 비밀번호를 credential 이라고 부른다.

 - 이중 인증

  • 한 번에 두 가지 방식으로 인증을 받는 것이다. 예를 들어 금융, 은행 웹 애플리케이션을 이용해 온라인 거래를 할 때에는 로그인과 보안인증서 두 가지 방법으로 인증 받는다. 인증(Authentication)이 하나 추가됨으로서 프로그래밍적으로 변화해야할 부분은 광범위해진다.

 - 물리적인 인증

  • 웹의 영역을 벗어난 것이지만 가장 효과적인 보안 수단 중 하나이다. 예를 들어 컴퓨터를 켤 때 지문을 인식 받거나 키를 삽입해야 하는 것들 이다.

 

권한 부여 (Authorization)

 - 사용자를 인증한 후에는 어떤 종류의 작업을 수행할 수 있는지 결정해야한다. 

 - 널리 사용되는 옵션은 API와 연관된 여러 역할을 갖는 것이다. 역할과 관련된 다양한 API 작업에 권한을 가질 수 있다.

 - 사용자에게 작업을 수행할 수 있는 권한이 있는지 확인하는 프로세스를 권한 부여 라고 한다.

 - 알반적으로 권한은 역할(role) 형태로 승인된다.

  • 유효한 사용자가 작업을 수행할 수 있을까 ?

권한 부여(Authorization)의 종류

 - 부여된 권한 (Granted Autority)

  • 적절한 절차로 사용자가 인증되었다면 권한을 부여해야한다. 회원가입 등을 통해 반영구적인 권한이 부여되었다면 이 회원에게 부여된 권한을 어딘가에 저장해야한다. 만약 해당 사용자가 로그인 했을 때 메인 페이지로 넘어갈 수 없다면 권한 부여에 문제가 있는 것이다.

 - 리소스의 권한 (Intercept)

  • 사용자의 권한만 있다고 보안이 제대로 동작하지 않는다. 보안이란, 원래 권한이 없는 이들이 원천적으로 리소스에 접근할 수 없도록 막아내는 것이다. 그런 의미에서 적절한 권한을 가진 이들만 해당 자원에 접근할 수 있도록 자원의 외부 요청을 원천적으로 가로채는 것(Intercept)이 웹 보안 이다.

스프링에서 제공하는 기본 권한 (Authority)

  • ROLE_ANONYMOUS : 모든 사용자
  • IS_AUTHENTICATED_ANONYMOUSLY : 익명 사용자
  • IS_AUTHENTICATED_FULLY : 인증된 사용자
  • IS_AUTHENTICATED_REMEMBERED : REMEMBERED 사용자
  • ROLE_RESTRICTED : 제한된 사용자
  • ROLE_USER : 일반 사용자
  • ROLE_ADMIN : 관리자

 

스프링 시큐리티 활용 예제

Spring Security를 적용하기 위해 web.xml 설정

  • <filter-name> 태그 값은 반드시 springSecurityFilterChain 이어야 한다.
    • Spring Security는 자기 자신을 DelegatingFilterProxy와 연결하기 위해 springSecurityFilterChain 라는 필터 이름을 요구한다.
  • 모든 요청 URL(/*) 에 DelegatingFilterProxy 필터를 적용하고 있다. DelegatingFilterProxy는 spring-security-context.xml을 통해 빈 중에서 이름이 springSecurityFilterChain인 빈을 참조하게 된다. 이렇게 DelegatingFilterProxy 와 Spring Security 가 연결된다.
<!-- 스프링 시큐리티 -->
<filter>
	<filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

 

web.xml 의 contextConfigLocation 에서 정의된 security-context.xml 추가

<context-param>
	<param-name>contextConfigLocation</param-name>
		<param-value>
			...
			/WEB-INF/spring/security-context.xml
		</param-value>
	</context-param>

 

security-context.xml

<?xml version="1.0" encoding="UTF-8"?>  
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/security
		http://www.springframework.org/schema/security/spring-security.xsd">

 	
    <!-- 권한이 없는 페이지를 접근할 경우 접근 불가 메시지 출력  -->
	<http auto-config="true">
		<!-- spring 4.x에 추가된 옵션으로 ssl을 사용하지 않을 경우 csrf는 disalbed=true로 설정. -->
		<csrf disabled="true"/>
		
		<!-- 모든 유저 항상 승인 -->
		<intercept-url pattern="/" access="permitAll"/>
		<intercept-url pattern="/index.jsp" access="permitAll"/>
		<intercept-url pattern="/member/login" access="permitAll"/>
		<intercept-url pattern="/resources/**" access="permitAll"/>
		<!-- 하나의 권한 설정 -->
		<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/>
		<!-- 하나 이상의 권한 설정 -->
		<intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
		
		<!-- 사용자 정의 로그인 폼 -->
		<form-login login-page="/member/login"
			login-processing-url="/member/login_check"
			authentication-failure-url="/member/login_check?login_error"
			username-parameter="userId"
			password-parameter="userPwd"
			default-target-url="/"
			always-use-default-target="true"
		/>
		
		<logout logout-url="/member/logout"
			invalidate-session="true"
			logout-success-url="/"
		/>
		
		<!-- 접근 권한이 없는 경우 -->
		<access-denied-handler error-page="/member/noAuthorized"/>
		
		<!-- 동일 아이디로 1명만 접속(기존 세선 만료) -->
		<session-management>
			<concurrency-control max-sessions="1"/>
		</session-management>
		
	</http>
    
	<authentication-manager>
		<authentication-provider>
			<user-service>
				<!-- noop: 텍스트 그대로 패스워드를 인식! security 5.부터 -->
				<user name="admin" password="{noop}admin" authorities="ROLE_ADMIN, ROLE_USER"/>
				<user name="spring" password="{noop}spring" authorities="ROLE_USER"/>
			</user-service>
		</authentication-provider>
	</authentication-manager>

</beans:beans>

 

 

일반 사용자(spring)가 관리자 권한을 요구하는 기능을 실행할 때 접근 권한이 없으므로 noAuthorized.jsp 가 출력된다.