[Spring] Spring Security 기본 세팅과 로그인, 로그아웃 로직
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