Spring

[Spring] Spring Security 기본 세팅과 로그인, 로그아웃 로직

jane.dev 2021. 10. 9. 12:49
반응형
Spring Security
서블릿의 여러 종류의 filter와 interceptor를 이용해 
애플리케이션에 인증 및 기타 보안 기능을 제공하는 Java/Java EE 프레임워크

 

pom.xml

Spring Security 관련 라이브러리 추가(Maven Repository)

Spring Security Web

 

Spring Security Config

 

Spring Security Core

 

 

Spring Security Taglibs

 

security-context.xml

(webapp/WEB-INF/spring 내부)

Spring Security 를 단독으로 설정하기 위해 에 별도의 Spring Security bean 관리 컨테이너 생성

 

Namespaces에 security 체크

 

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/security
		http://www.springframework.org/schema/security/spring-security.xsd
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

beans 태그 스키마 수정

 

web.xml

Spring Security가 filter를 이용해 스프링의 동작에 관여하도록 설정(→ 한글 인코딩 관련 필터 아래에 작성되어야함)

<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>

에러는 springSecurityFilterChain 이라는 빈이 제대로 설정되지 않았기 때문인데, 이유는 Spring Security 설정 파일을 찾을 수 없어서 발생

 

<context-param>
    <param-name>contextConfigLocation</param-name>
    <!-- security-context.xml을 인식할 수 있도록 설정 -->
    <param-value>/WEB-INF/spring/root-context.xml
    /WEB-INF/spring/security-context.xml
    </param-value>
</context-param>

 

security-context.xml

스프링이 동작하기 위해서 Authentication Manager가 존재해야하며, Spring Security의 시작 지점이 필요하기 때문에 아래와 같이 추가

<!-- 시작지점 -->
<security:http>
    <security:form-login/>
</security:http>
<!-- 인증 매니저 -->
<security:authentication-manager>
</security:authentication-manager>

 

SecurityController

Spring Security에 의해 제어가 필요한 URI 설계 → 해당 URI에 맞는 메서드를 작성

@Controller
public class SecurityController {

	// 로그인을 하지 않은 사용자도 접근이 가능
	@GetMapping("/all")
	public void doAll() {
		log.info("모든 사람이 접속 가능한 all 로직");
	}
	
	// 로그인을 한 사용자들만 접근이 가능
	@GetMapping("/member")
	public void doMember() {
		log.info("회원들이 접속 가능한 member 로직");
	}
	
	// 로그인을 한 사용자들중에서 관리자 권한을 가진 사용자만 접근이 가능
	@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
	@GetMapping("/admin")
	public void doAdmin() {
		log.info("운영자만 접속 가능한 admin 로직");
	}
}

 

view 폴더 아래 해당 URI에 맞는 화면을 작성(먼저 내용만 구분할 수 있도록 작성)

all.jsp

<body>
    <h1>all</h1>
</body>

 

member.jsp

<body>
    <h1>member</h1>
</body>

 

admin.jsp

<body>
    <h1>admin</h1>
</body>

 

Spring Security의 동작과정

인증(Authentication) 자신을 증명하는 것
권한부여(인가, Authorization) 남에 의해서 자격이 부여되는 것

위에서 security-context.xml 에 추가한 인증매니저(AythenticationManager)가 인증을 담당하며

인증에 대한 처리는 인증제공자(AuthenticationProvider)라는 객체가 위임받아서 실제 인증 작업을 진행

이때, 인증된 정보에는 권한에 대한 정보를 같이 전달하게 되는데 이 처리는 UsserDetailsService 인터페이스를 구현함으로써 실제 사용자의 정보와 사용자가 가진 권한의 정보를 처리해서 반환

 

로그인과 로그아웃을 처리하기 위해서 먼저 앞에서 설계한 URI에 대한 접근 제한을 설정

security-context.xml

<security:http>
    <!-- 인터셉터를 이용해 특정 URI에 접근을 제한 -->
    <security:intercept-url pattern="/all" access="permitAll"/>
    <security:intercept-url pattern="/member" access="hasRole('ROLE_MEMBER')"/>
    <security:intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')"/>
</security:http>

pattern 속성에는 URI패턴을, access 속성에는 권한을 작성

→ '/member' 와 '/admin'으로 접근하면 로그인 페이지('/login', Spring Security가 기본으로 제공하는 페이지)로 강제이동됨

 

security-context.xml

DB를 거치지 않고 인증과 권한을 통한 로그인 처리가 되는지 확인하기 위해서 임시로 아이디와 비밀번호 생성

ROLE_MEMBER

<security:authentication-manager>
    <security:authentication-provider user-service-ref="customUserDetailsService">
        <security:user-service>
            <!-- {no option}을 작성해 비밀번호 복호화 강제처리를 off -->
            <security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>
        </security:user-service>
    <security:authentication-provider user-service-ref="customUserDetailsService">
<security:authentication-manager>

인증 매니저내부에 임시 아이디와 비밀번호를 작성하는데, Spring Security에서 PasswordEncoder 지정이 요구하기 때문에 비밀번호 복호화 포맷팅 처리 없이 확인하기 위해서 패스워드 앞에 {noop} 문자열 추가

→ member 권한을 가진 사용자만 접근이 가능했던 '/member'에도 접속이 가능한 것을 확인할 수 있음

 

ROLE_ADMIN

<security:authentication-manager>
    <security:authentication-provider user-service-ref="customUserDetailsService">
        <security:user-service>
            <!-- admin 계정에는 권한을 두개 작성해 부여 -->
            <security:user name="admin" password="{noop}admin" authorities="ROLE_MEMBER, ROLE_ADMIN"/>
        </security:user-service>
    <security:authentication-provider user-service-ref="customUserDetailsService">
<security:authentication-manager>

→ admin 계정은 '/member' 와 '/admin' 둘 다 접근이 가능

 

security-context.xml

로그아웃도 로그인과 마찬가지로 특정 URI를 지정

<security:http>
    <!-- 로그아웃 시 세션을 파기하고 특정한 쿠키를 지우는 작업을 지정 -->
    <security:logout logout-url="/customLogout" invalidate-session="true"
		delete-cookies="remember-me, JSESSIONID" />
</security:http>

 

CommonController

@Controller
public class CommonController {
    // 로그아웃 폼으로 이동
	@GetMapping("/customLogout")
	public void logoutGet() {
		log.info("로그아웃 폼으로 이동");
	}
    
	// 로그아웃 처리
	@PostMapping("/customLogout")
	public void logoutPost() {
		log.info("post 방식으로 로그아웃 요청 처리");
	}
}

GET 방식으로 로그아웃을 결정하는 페이지로 이동해 POST 방식으로 처리

 

view 폴더 아래 해당 URI에 맞는 화면을 작성

customLogout.jsp

<body>
	<form action="/customLogout" method="post">
		<!-- 해당 페이지를 통해서만 로그아웃이 이루어지도록 하고 form 에는 항상 _csrf token 작성 -->
		<input type="hidden" name="${_csrf.parameterName }" value="${_csrf.token }" />
		<input type="submit" value="LOGOUT" />
	</form>
</body>

member.jsp / admin.jsp에 '/customLogout'으로 이동하는 링크를 만들어 실행하면 해당 페이지로 이동해와서 로그아웃이 동작하는 것을 볼 수 있음

 

2021.11.07 - [Spring] - [Spring] CSRF 공격과 token을 사용한 방어

 

[Spring] CSRF 공격과 token을 사용한 방어

CSRF(Cross-Site Request Forgery) 공격 사이트 간 요청 위조라고도하며, 웹사이트 취약점 공격의 일종 사용자가 자신의 의지와 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 하는것 이러

wheneveryouwantsz.tistory.com