사용자 이름/비밀번호 인증

Jaewoo Back·2024년 7월 12일
0

Spring Security공부

목록 보기
6/13
post-thumbnail

사용자를 인증하는 가장 일반적인 방법 중 하나는 사용자 이름과 암호를 확인하는 것입니다. Spring Security는 사용자 이름 및 암호를 사용한 인증에 대한 지원을 합니다.

간단한 사용자 이름/비밀번호 예

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.httpBasic(Customizer.withDefaults())
			.formLogin(Customizer.withDefaults());

		return http.build();
	}

	@Bean
	public UserDetailsService userDetailsService() {
		UserDetails userDetails = User.withDefaultPasswordEncoder()
			.username("user")
			.password("password")
			.roles("USER")
			.build();

		return new InMemoryUserDetailsManager(userDetails);
	}

}

AuthenticationManager

사용자 정의 인증을 위한 Bean 공개AuthenticationManager

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.requestMatchers("/login").permitAll()
				.anyRequest().authenticated()
			);

		return http.build();
	}

	@Bean
	public AuthenticationManager authenticationManager(
			UserDetailsService userDetailsService,
			PasswordEncoder passwordEncoder) {
		DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
		authenticationProvider.setUserDetailsService(userDetailsService);
		authenticationProvider.setPasswordEncoder(passwordEncoder);

		return new ProviderManager(authenticationProvider);
	}

	@Bean
	public UserDetailsService userDetailsService() {
		UserDetails userDetails = User.withDefaultPasswordEncoder()
			.username("user")
			.password("password")
			.roles("USER")
			.build();

		return new InMemoryUserDetailsManager(userDetails);
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}

}

위의 구성을 사용하면 @RestController를 만들 수 있습니다.

@RestController

package com.example.hellosecurity.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {

    private final AuthenticationManager authenticationManager;

    public LoginController (AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @PostMapping("/login")
    public ResponseEntity<Void> login(@RequestBody LoginRequest loginRequest) {
        Authentication authenticationRequest =
                UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.username(), loginRequest.password());
        Authentication authenticationResponse =
                this.authenticationManager.authenticate(authenticationRequest);
        // ...
        return ResponseEntity.ok().build();
    }

    public record LoginRequest(String username, String password) {
    }
}


사용자 지정 AuthenticationManager

일반적으로 Spring Security는 사용자 이름/암호 인증을 위해 내부적으로 구성된AuthenticationManager를 빌드합니다. 사용하는 인스턴스를 사용자 정의하는 것이 여전히 필요할 수 있습니다. 예를 들어 캐시된 사용자에 대해 자격 증명 지우기를 사용하지 않도록 설정해야 할 수 있습니다.


@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		//...
	}

	@Bean
	public AuthenticationManager authenticationManager(
			UserDetailsService userDetailsService,
			PasswordEncoder passwordEncoder) {
		DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
		authenticationProvider.setUserDetailsService(userDetailsService);
		authenticationProvider.setPasswordEncoder(passwordEncoder);

		ProviderManager providerManager = new ProviderManager(authenticationProvider);
		providerManager.setEraseCredentialsAfterAuthentication(false);

		return providerManager;
	}

	@Bean
	public UserDetailsService userDetailsService() {
		//...
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}

}

또는 Spring Security의 전역을 빌드하는 데 다음과 같이 빌더를 구성할 수 있습니다.

전역 구성 AuthenticationManagerBuilder

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		// ...
		return http.build();
	}

	@Bean
	public UserDetailsService userDetailsService() {
		// Return a UserDetailsService that caches users
		// ...
	}

	@Autowired
	public void configure(AuthenticationManagerBuilder builder) {
		builder.eraseCredentials(false);
	}

}

로그인 검증 로직

DaoAuthenticationProvider는 UserDetailsServicePasswordEncoder를 사용하여 사용자 이름과 암호를 인증하는 AuthenticationProvider 구현입니다.

로그인 검증 로직을 작성하기 위해 UserDetailsService를 구현해보겠다.

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        UserEntity userData = userRepository.findByUsername(username);

        if (userData != null) {

            return new CustomUserDetails(userData);
        }

        return null;
    }
}

DaoAuthenticationProvider 에서 UserDetails를 조회합니다. UserDetailsService

UserDetailsService에서 UserDetails를 반환합니다. DaoAuthenticationProvider는 유효성을 검사한 다음 구성된 보안 주체가 있는 인증을 반환합니다.


UserDetails 인터페이스 구현

package com.example.testsecurity.config;

import com.example.testsecurity.entity.UserEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class CustomUserDetails implements UserDetails {

   private UserEntity userEntity;

   public CustomUserDetails(UserEntity userEntity) {
       
       this.userEntity = userEntity;
   }

   @Override
   public Collection<? extends GrantedAuthority> getAuthorities() {
       Collection <GrantedAuthority> authorities = new ArrayList<>();
       authorities.add(new GrantedAuthority() {
           
           @Override
           public String getAuthority() {
               return userEntity.getRole();
           }
       });
       
       return authorities;
   }

   @Override
   public String getPassword() {
       return userEntity.getPassword();
   }

   @Override
   public String getUsername() {
       return userEntity.getUsername();
   }
}

정리

로그인 요청이 들어오면 Security Config는 유효한 아이디인지 검사한다.
1. UserDetailsService 에서 user정보를 로드하여 UserDetails 객체를 뱉는다.
2. 생성된 userDetails 객체는 DaoAuthenticationProvider에 유효성을 검사한 다음 구성된 보안 주체가 있는 인증을 반환합니다

profile
https://blog.naver.com/jaewoo2_25

0개의 댓글