본문 바로가기
SpringSecurity

스프링 시큐리티 복습 3 - 인증 프로세스 Form 인증 구현

by 서영선 2023. 8. 22.

 

 

 

1.   환경 설정 

기본 의존 관계 설정  -  pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
     <groupId>org.thymeleaf.extras</groupId>
     <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

 

기본 환경 설정  -  application.properties

spring.datasource.url=jdbc:postgresql://localhost:5432/springboot
spring.datasource.username=postgre
spring.datasource.password=1234

spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

spring.thymeleaf.cache=false

spring.devtools.livereload.enabled=true
spring.devtools.restart.enabled=true

 

SecurityConfig 설정

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
    @Override
    protected void configure(HttpSecurity http) throws Exception{
    	http
        	.authorizeRequests()
            .anyRequest().authenticated()
        .and()
        	.formLogin();
        }
    }
}

 

 

 

 

 

 

 

 

2.  메뉴 권한 및 WebIgnore 설정

WebIgnore 설정 : js / css / image 파일 등 보안 필터를 적용할 필요가 없는 리소스 설정

@Override
public void configure(WebSecurity web) throws Exception{
	web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}

 

 

 

 

 

 

 

3.  Form 인증  -  User 등록 / PasswordEncoder

- Spring Security 5.0 이전에는 기본 PasswordEncoder가 평문을 지원하는 NoOpPasswordEncoder

 

- 암호화 포맷 : {id}encodedPassword

: bcrypt, noop, pbkdf2, scrypt, sha256 ( 기본 포맷은 Bcrypt )

 

 

- 생성 : PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()

 

- 인터페이스 :

  encode(password) 패스워드 암호화

  matches(rawPassword, encodedPassword) 패스워드 비교

 

 

 

 

 

 

 

 

4. Form 인증  -  CustomUserDetailsService

public UserDetails loadByUsername(String username) throws UsernameNotFoundException {

	Account account = userRepository.findByUsername(username);
    
    if (account == null){
    	throw new UsernameNotFoundException("No user found with username: "+ username);
    }
    // 권한
    ArrayList<Granted Authority> roles = new ArrayList<>();
    roles.add(new SimpleGrantedAuthority(account.getRole()));
    
    return new AccountContext(account, roles);
    
}

 

 

 

 

 

 

 

5. Form 인증  -  CustomAuthenticationProvider

public Authentication authenticate(Authentication auth) throws AuthenticationCredentialsNotFoundException {

    String loginId = auth.getName();
    String passwd = (String)auth.getCredentials();

    UserDetails userDetails = userDetailsService.loadUserByUSername(loginId);

    if(userDetails == null || !passwordEncoder.matches(passwd, userDetails.getPassword())){
        throw new BadCredentialsException("Invalid password");
    }

    return new UsernamePasswordAuthenticationToken(userDetails.getUser(), null, userDetails.getAuthorities());
}

 

 

 

 

 

 

 

 

6. Form 인증  -  Custom Login Form Page

Spring Security 가 제공하는 기본 /login 페이지가 아닌 다른 로그인 페이지를 사용하기 위한 설정

@Override
public void configure(HttpSecurity http) throws Exception{
    http.formLogin().loginPage("/customLogin");
}

 

 

 

 

 

 

 

7.  Form 인증  -  로그아웃 및 화면 보안 처리

  • 로그아웃 방법
    <form> 태그를 사용해서 POST로 요청
    <a> 태그를 사용해서 GET으로 요청 - SecurityContextLogoutHandler 활용
  • 인증 여부에 따라 로그인 / 로그아웃 표현
    <li sec:authorize="isAnonymous()"> <a th:href="@{/login}"> 로그인 </a> </li>
    <li sec:authorize="isAuthenticated()"> <a th:href="@{/logout}"> 로그아웃 </a> </li>

 

@GetMapping("/logout")
public String logout(HttpServletRequest request, HttpServletResponse response){

    Authentication auth = SecurityContextHolder.getContext().getAuthentication();

    if(auth != null){
        new SecurityContextLogoutHandler().logout(request,response,auth);
    }

    return "redirect:/login";
}

 

 

 

 

 

 

 

8. Form 인증  -  WebAuthenticationDetails,  AuthenticationDetailsSource

  • WebAuthenticationDetails : 인증 과정 중 전달된 데이터를 저장, Authentication 의 details 속성에 저장
  • AuthenticationDetailsSource : WebAuthenticationDetails 객체를 생성

 

 

 

 

 

9.  Form 인증  -  CustomAuthenticationSuccessHandler,  CustomAuthenticationFailureHandler

 

< SecurityConfig >

@Override
public void configure(HttpSecurity http) throws Exception{
	http
    	.formLogin()
        .successHandler(CustomAuthenticationSuccessHandler())
        .failureHandler(CustomAuthenticationFailureHandler())
}

 

< CustomAuthenticationSuccessHandler >

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
	
    RequestCache requestCache = new HttpSessionRequestCache(); // 요청 캐시와 관련된 작업
    final HttpSession session = request.getSession(false);  // 세션 관련 작업
    Object principal = authentication.getPrincipal(); // 인증된 사용자 관련 작업
    redirectStrategy.sendRedirect(request, response, targetUrl); // 인증 성공 후 이동

}

 

< CustomAuthenticationFailureHandler >

@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {

	if(exception instanceOf UsernameNotFoundException){
    	errorMessage = messages.getMessage("사용자가 존재하지 않습니다.", null, locale);
    }
    else if (exception instanceof BadCredentialsException) {
    	errorMessage = messages.getMessage("아이디 혹은 비밀번호가 일치하지 않습니다.", null, locale);
    }
    else{
    	errorMessage = "인증에 실패하였습니다. 웹 마스터에게 문의하십시오!";
    }
    
}

 

 

 

 

 

 

 

 

 

 

 

 

 

10.  Form 인증  -  Access Denied

< SecurityConfig >

@Override
public void configure(HttpSecurity http) throws Exception {
	http.exceptionHandling().accessDeniedPage("/accessDenied")
    	.accessDeniedHandler(accessDeniedHander)
    }
}

 

< AccessDeniedHandler >

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
	String deniedUrl = errorPage + "?exception=" + accessDeniedException.getMessage();
    response.sendRedirect(request, response, deniedUrl);
}

public void setErrorPage(String errorPage) {
	this.errorPage = errorPage;
}

 

 

 

 

 

 

 

 

 

 

 

11. Form 인증  -  인증 사용자 정보 구하기

1. 어플리케이션 전역

Account account = (Account) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = account.getUsername();

2. Spring MVC

  • Authentication, Principal
public String getUsername(Authentication authentication, Principal principal) {
	Account account = (Account) authentication.getPrincipal();
    Account account = (Account) ((UsernamePasswordAuthenticationToken) principal).getPrincipal();
    String username = account.getUsername();
}

 

  • @AuthenticationPrincipal

: 인증 성공 이후 생성된 Authentication 객체의 principal 속성에 저장되어 있는 객체


public ModelAndView getUsername(@AuthenticationPrincipal Account account) {
	String username = account.getUsername();
}

 

 

 

 

 

 

 

 

 

 

 

 

댓글