스프링 게시판 만들기 - 5 (1) ( 로그인 처리 feat.스프링 시큐리티)
스프링 게시판 만들기 -4 에서는 회원가입을 하고 , 가입된 결과가 DB 에 잘 저장되는지 확인 했었다.
이제는 회원가입한 결과를 바탕으로 로그인을 해보자. ( 스프링 시큐리티를 사용 )
그 전에 스프링 시큐리티에 대해 간단하게(?) 알아보고 코드를 작성해보자.
(코드는 스프링 게시판 만들기 -5 (2) 에서 확인)
스프링 시큐리티
스프링 시큐리티란 ?
- 스프링 시큐리티는 스프링 기반의 어플리케이션의 보안(인증과 권한)을 담당하는 프레임워크이다.
- 스프링 시큐리티는 보안과 관련해서 체계적으로 많은 옵션들로 이를 지원해준다.
- spring security는 filter 기반으로 동작하기 때문에 spring MVC 와 분리되어 관리 및 동작한다.
( * security 3.2부터는 XML로 설정하지 않고도 자바 bean 설정으로 간단하게 설정할 수 있도록 지원 )
(filter 에 대해서는 아래에서 설명)
-> 기존의 session 과 쿠키 관련 코드로 로그인을 처리 하던 경우에 비해 효율성을 높였다.
( 문제는 사용하기 어렵다는 것 )
주요 용어
- 접근 주체(Principal) : 보호된 리소스에 접근하는 대상
- 인증(Authentication) : 보호된 리소스에 접근한 대상에 대해 이 유저가 누구인지, 애플리케이션의 작업을 수행해도 되는 주체인지 확인하는 과정(ex. Form 기반 Login)
- 인가(Authorize) : 해당 리소스에 대해 접근 가능한 권한을 가지고 있는지 확인하는 과정(After Authentication, 인증 이후)
- 권한 : 인증된 주체가 애플리케이션의 동작을 수행할 수 있도록 허락되있는지를 결정
- 권한 승인이 필요한 부분으로 접근하려면 인증 과정을 통해 주체가 증명 되어야만 한다
- 권한 부여에도 두가지 영역이 존재하는데 웹 요청 권한, 메소드 호출 및 도메인 인스턴스에 대한 접근 권한 부여
1. 요청 수신
- 브라우저에서 사용자가 아이디와 비밀번호를 입력하고 요청을 보냄 (HttpRequest)
2. 토큰 생성
- AuthenticationFilter가 요청을 받아서 UsernamePasswordAuthenticationToken토큰을 생성UsernamePasswordAuthenticationToken은 해당 요청을 처리할 수 있는 Provider을 찾는데 사용
3. Authentication Manager에게 처리 위임
- Authentication Manager는 List형태로 Provider들을 갖고 있음ProviderManager가 갖고 있는 Provider들을 차례로 탐색하면서 각 Provider들의 supports메소드로 확인
4. Token을 처리할 수 있는 Authentication Provider 선택
- supports메소드를 호출하여 처리 가능한 Provider선택
5. UserDetailsService의 loadUserByUsername메소드 수행
- UserDetailsService를 구현하여 loadUserByUsername를 오버라이딩
- loadUserByUsername메소드 안에 파일이던 DB던 유저를 찾는 로직을 정의
6. DB등 사용자의 정보가 저장 된 곳에서 데이터를 꺼내어 UserDetails형태로 반환
7. 인증 완료 시 Authentication객체를 SecurityContextHolder안에 저장
- 필터
Request 가 들어오면 Filter 에 따라 걸러지고 인증된 사용자만 Response 로 호출되는 방식.
필터에 수 많은 기능이 있지만 단순 로그인 처리만 할 예정이기에.. 자세한 공부는 다음에 해보는 걸로..
간단한 구현 단계에서는 별도의 로직이 크게 필요하지 않기에 너무 겁먹지 말고 시도해보자..
우선 스프링 시큐리티 (Spring Security) 를 사용하기 위해서는 gradle 에 다음 두 코드 추가.
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
/board/config 디렉토리 생성 -> SecurityConfig 파일 생성
스프링 시큐리티 설정은 WebSecurityConfigurerAdapter라는 클래스를 상속받은 클래스에서
메서드를 오버라이딩하여 조정할 수 있다.
- SecurityConfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package toyproject.board.config;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import toyproject.board.Service.MemberService;
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final MemberService memberService;
// 비밀번호 암호화
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
// 인증 무시 -> static 디렉토리의 파일들은 항상 인증 무시 (통과)
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
}
//Request 가 들어오는 경우 권한 설정 & 로그인 & 로그아웃 처리
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/member/myinfo").hasRole("USER")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/member/memberLoginForm")
.defaultSuccessUrl("/member/memberLoginResult")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/member/logout"))
.logoutSuccessUrl("/member/logoutResult")
.invalidateHttpSession(true)
.and()
.exceptionHandling().accessDeniedPage("/member/denied");
}
// 모든 인증을 처리하기 위한 AuthenticationManager
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService).passwordEncoder(passwordEncoder());
}
}
|
cs |
- @Configuration : 컴포넌트 스캔을 위한 어노테이션 (설정 파일임을 표현)
- @EnableWebSecurity : 스프링 시큐리티를 사용하기 위한 어노테이션
- WebSecuirtyConfigurerAdapter 를 상속받아서 여러 메소드를 오버라이딩.
- passwordEncoder() -> 비밀번호를 암호화 할 수 있도록 하는 메소드
- Configure(WebSecurity web) -> WebSecurity 는 FilterChainProxy 를 생성하는 필터
web.ignoring -> 해당 경로의 파일들은 스프링 시큐리티가 무시할 수 있도록 설정한다.
- configure(HttpSecurity http) : HttpSecurity를 통해 HTTP 요청에 대한 웹 기반 보안을 구성할 수 있습니다.
- authorizeRequests()
HttpServletRequest에 따라 접근(access)을 제한합니다.
antMatchers() 메서드로 특정 경로를 지정하며, permitAll(), hasRole() 메서드로 역할(Role)에 따른 접근 설정을 잡아줍니다. 여기서 롤은 권한을 의미합니다. 즉 어떤 페이지는 관리지만 접근해야 하고, 어떤 페이지는 회원만 접근해야할 때 그 권한을 부여하기 위해 역할을 설정하는 것입니다.
예를 들어,
.antMatchers("/admin/**").hasRole("ADMIN")
/admin 으로 시작하는 경로는 ADMIN 롤을 가진 사용자만 접근 가능합니다.
.antMatchers("/user/myinfo").hasRole("MEMBER")
/user/myinfo 경로는 MEMBER 롤을 가진 사용자만 접근 가능합니다.
.antMatchers("/**").permitAll()
모든 경로에 대해서는 권한없이 접근 가능합니다
.
.anyRequest().authenticated()
모든 요청에 대해, 인증된 사용자만 접근하도록 설정할 수도 있습니다.
- formlogin()
form 기반으로 인증을 하도록 합니다. 로그인 정보는 기본적으로 HttpSession을 이용합니다.
/login 경로로 접근하면, Spring Security에서 제공하는 로그인 form을 사용할 수 있습니다.
- .loginPage("/user/login")
기본 제공되는 form 말고, 커스텀 로그인 폼을 사용하고 싶으면 loginPage() 메서드를 사용합니다.
이 때 커스텀 로그인 form의 action 경로와 loginPage()의 파라미터 경로가 일치해야 인증을 처리할 수 있습니다. ( login.html에서 확인 )
- .defaultSuccessUrl("/user/login/result")
로그인이 성공했을 때 이동되는 페이지이며, 마찬가지로 컨트롤러에서 URL 매핑이 되어 있어야 합니다.
- .usernameParameter("파라미터명")
로그인 form에서 아이디는 name=username인 input을 기본으로 인식하는데, usernameParameter() 메서드를 통해 파라미터명을 변경할 수 있습니다.
- logout()
로그아웃을 지원하는 메서드이며, WebSecurityConfigurerAdapter를 사용할 때 자동으로 적용됩니다.
기본적으로 "/logout"에 접근하면 HTTP 세션을 제거합니다. - .logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
로그아웃의 기본 URL(/logout) 이 아닌 다른 URL로 재정의합니다.
- .invalidateHttpSession(true)
HTTP 세션을 초기화하는 작업입니다.
- deleteCookies("KEY명")
로그아웃 시, 특정 쿠기를 제거하고 싶을 때 사용하는 메서드입니다.
- .exceptionHandling().accessDeniedPage("/user/denied");
예외가 발생했을 때 exceptionHandling() 메서드로 핸들링할 수 있습니다.
프로젝트에서는 접근권한이 없을 때, 로그인 페이지로 이동하도록 명시해줬습니다
- configure(AuthenticationManagerBuilder auth)
Spring Security에서 모든 인증은 AuthenticationManager를 통해 이루어지며 AuthenticationManager를 생성하기 위해서는 AuthenticationManagerBuilder를 사용합니다.
로그인 처리 즉, 인증을 위해서는 UserDetailService를 통해서 필요한 정보들을 가져오는데, 예제에서는 서비스 클래스(memberService)에서 이를 처리합니다.
서비스 클래스에서는 UserDetailsService 인터페이스를 implements하여, loadUserByUsername() 메서드를 구현하면 됩니다.
원래 이번 장에서 구현한 코드도 모두 설명하려 했으나 생각보다 내용이 길어져서 다음 장으로 넘길까 한다.
스프링 시큐리티. 잘 쓰면 편리할 것 같지만 내용이 생각보다 너무 방대하고 세션 쿠키 등 기본적인 개념이 있어야 하기 때문에 다음 챕터에서 스프링 시큐리티를 정말 간단하게 이용하여 로그인 처리만 가능케 해보도록 하겠습니다.
다음편!
https://dodokong.tistory.com/27?category=1249752