(Spring Security) 인증 구현

soosoorim·2024년 5월 8일
0

인증 구현 - 정책 설정

  • SecurityAuthenticationProvider.java - 인증 공급자
/**
 * <pre>
 * UserDetailsService 와 PasswordEncoder을 이용해서
 * 로그인을 요청한 사용자의 인증을 처리한다.
 * 
 * UserDetailsService : SecurityUserDetailsService
 * PasswordEncoder : SecuritySHA
 * 
 * 인증에 성공한 경우, SecurityContext에 인증정보를 저장해야 한다.
 * 
 * AuthenticationProvider 인터페이스를 구현해야 한다.
 * </pre>
 */
@Component
public class SecurityAuthenticationProvider implements AuthenticationProvider {
	
	/**
	 * SecurityUserDetailsService -> DI
	 */
	@Autowired
	private UserDetailsService userDetailsService;
	
	/**
	 * SecuritySHA -> DI
	 */
	@Autowired
	private PasswordEncoder passwordEncoder;

	/**
	 * Spring Security 인증 수행.
	 * @param authentication 로그인에 사용된 인증 정보들.
	 * @return 아이디와 비밀번호가 일치하는 회원의 정보 (UsernamePasswordAuthenticationToken)
	 */
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		
		// 인증 시작.
		// 로그인에 사용된 아이디(이메일)
		String email = authentication.getName();
		
		// 로그인에 사용된 비밀번호
		String password = authentication.getCredentials().toString();
		
		// UserDetailsService에서 이메일 정보로 회원의 정보를 조회한다.
		// 만약, 회원의 정보가 존재하지 않다면, UsernameNotFoundException이 던져진다.
		// UserDetails ==> SecurityUser
		UserDetails userDetails =  this.userDetailsService.loadUserByUsername(email);
		
		// 비밀번호 암호화를 위해서 DB에 저장되어있던 salt를 조회한다.
		// salt 값은 MemberVO 내부에 있고
		// Spring Security 과정 내부에서는 UserDetails에 있고
		// UserDetails를 상속한 SecurityUser 가 있다!
		// UserDetails를 SecurityUser로 변환하여 salt를 얻어온다.
		String salt = ((SecurityUser) userDetails).getMemberVO().getSalt();
		
		// PasswordEncoder로 로그인에 사용된 비밀번호를 암호화.
		// Spring Security 과정 내부에서 PasswordEncoder는 SecuritySHA로 정의.
		// SecuritySHA에는 암호화를 위한 salt 변수를 가지고 있다.
		// 조회된 salt 값을 SecuritySHA에 할당을 해야한다.
		// PasswordEncoder를 SecuritySHA로 변환하여 salt를 할당한다.
		((SecuritySHA) this.passwordEncoder).setSalt(salt);
		
		// PasswordEncoder를 이용해서 암호화된 비밀번호와, 입력한 비밀번호가 같은지 비교.
		boolean isMatchPassword = this.passwordEncoder.matches(password, userDetails.getPassword());
		
		// 만약 같지 않다면, BadCredentialsException을 던진다.
		if ( ! isMatchPassword ) {
			throw new BadCredentialsException("아이디 또는 비밀번호가 일치하지 않습니다.");
		}
		
		// 같다면, UsernamePasswordAuthenticationToken을 반환.
		// Security Context에 인증 정보를 저장!
		return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
	}

	/**
	 * <pre>
	 * 인증 요청을 처리할 인증 방식 저장.
	 * 인증 요청을 처리할 인증 필터 타입 지정.
	 * </pre>
	 */
	@Override
	public boolean supports(Class<?> authentication) {
		// UsernamePasswordAuthenticationToken : 아이디 / 비밀번호 인증 방식
		return authentication.equals(UsernamePasswordAuthenticationToken.class);
	}
}
  • SecurityConfig.java - 로그인 정책 설정 - 로그인 페이지 변경
@Configuration
@EnableWebSecurity // Spring Security Filter 정책 설정을 위한 Annotation
public class SecurityConfig {

...생략
@Bean
	SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.authorizeHttpRequests(httpRequest -> httpRequest
				// /board/list 는 Security 인증 여부와 관계없이 모두 접근이 가능하다.
				.requestMatchers(AntPathRequestMatcher.antMatcher("/board/search"))
				.permitAll()
}

...생략

// 이 애플리케이션은 Form 기반으로 로그인을 하여
// 로그인이 완료되면, /board/search로 이동을 해야한다.
// 로그인 페이지 변경.
		http.formLogin(formLogin -> formLogin
											 // Spring Security 인증이 성공할 경우, LoginSuccessHandler가 동작되도록 설정.
											 .successHandler(new LoginSuccessHandler())
											 // Spring Security 인증이 실패할 경우, LoginFailureHandler가 동작되도록 설정.
											 .failureHandler(new LoginFailureHandler())
											 // Spring Security Login URL 변경
											 .loginPage("/member/login")
											 // Spring Security Login 처리 URL 변경
											 // SecurityAuthenticationProvider 실행 경로 지정
											 .loginProcessingUrl("/member/login-proc")
											 // 로그인ID가 전달될 파라미터 이름
											 .usernameParameter("email")
											 // 로그인PW가 전달될 파라미터 이름
											 .passwordParameter("password"));
		
		// CSRF 방어로직 무효화.
		http.csrf(csrf -> csrf.disable());
		
		return http.build();

로그인 실패 처리
com.example.demo.beans.security.handler 패키지 생성
LoginFailureHandler.java 파일 생성

package com.hello.forum.beans.security.handler;

import java.io.IOException;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * <pre>
 * Spring Security Login 에 실패했을 경우
 * 해당 이벤트를 감지해서 자동으로 실행되는 클래스.
 * 
 * AuthenticationFailureHandler 인터페이스를 구현!
 * </pre>
 */
public class LoginFailureHandler implements AuthenticationFailureHandler {

	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
											AuthenticationException exception) throws IOException, ServletException {
		
		// 인증 실패 메세지를 로그인 페이지에 노출 시키는 코드.
		String authenticationFailureMessage = exception.getMessage();
		
		// 로그인 페이지를 보여주고
		// 인증 실패 메세지를 보내준다.
		String loginPagePath = "/WEB-INF/Views/member/memberlogin.jsp";
		
		RequestDispatcher rd = request.getRequestDispatcher(loginPagePath);
		request.setAttribute("message", authenticationFailureMessage);
		rd.forward(request, response);
		
	}
}
  • 로그인 실패 정책 설정하기 - SecurityConfig.java
import com.example.demo.beans.security.handler.LoginFailureHandler;
@Configuration
@EnableWebSecurity // Spring Security Filter 정책 설정을 위한 Annotation
public class SecurityConfig {
    ... 생략 ...
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 로그인에 성공하면 "/board/list"로 이동하도록 함.
        http.formLogin(formLogin -> 
                formLogin.defaultSuccessUrl("/board/list")
                         // 로그인 실패했을 때 처리 방법
                         .failureHandler(new LoginFailureHandler())
                         .loginPage("/member/login")
					  .loginProcessingUrl("/member/login-proc")
                         .usernameParameter("email")
                         .passwordParameter("password"));
        return http.build();
    }
}

세션 관련 Interceptor 모두 제거 - 비 Security 요소 제거

  • 더이상 session 은 사용하지 않는다.

0개의 댓글

관련 채용 정보