간단하게 inMemory 방식의 인증 사용자를 등록하는 예제를 학습하다가 여러 오류를 마주하게 되고, 이를 해결하는 과정을 기록했습니다.
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/css/**", "/js/**", "/img/**").permitAll()
.antMatchers("/guest/**").permitAll()
.antMatchers("/member/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated();
http.formLogin()
.permitAll();
http.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("1234")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("1234")).roles("ADMIN");
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
위와 같은 소스로 security를 이용하려고 하면
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'webSecurityConfig': Requested bean is currently in creation: Is there an unresolvable circular reference?
라는 오류와 함께 실행되지 않습니다.
순환참조? 라고 생각해서 밑에 있던 @Bean을 제거해봤습니다.
// @Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
순환참조 오류는 나오지 않았지만, 입력한 것을 받아줄 객체가 없어 인식을 못 하는 것 같습니다.
// @Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("1234")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("1234")).roles("ADMIN");
}
@Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
(수정) 밑에 출력되는 내용은 Spring Security 의존성이 추가되면 생성되는 임시 패스워드입니다.
생성된 패스워드는 기본으로 사용할 수 있는 'user'계정의 패스워드입니다.
프로젝트 초기에 아무 계정도 없을 때 사용할 수 있는 임시 패스워드 역할을 합니다.
Using generated security password: 4a5f3ce3-c597-466f-bbe6-84a06a739360
This generated password is for development use only. Your security configuration must be updated before running your application in production.
라는 메시지와 함께 애플리케이션이 실행됩니다.
하지만, 로그인을 시도하면 위 사진과 같이
라는 메시지가 뜨면서 로그인이 안 되는 것을 확인할 수 있습니다.
--> (수정) 잘못된 패스워드를 입력하면 이렇게 됩니다.
o.a.c.util.SessionIdGeneratorBase : Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [124] milliseconds.
[SHA1PRNG]방식을 사용한 세션 ID 생성을 위한 SecureRandom 인스턴스 생성에 사용된 시간(milliseconds)이 얼만큼 있다~ 라는 메시지가 나옵니다.
o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
비밀번호가 BCrypt 방식으로 인코딩되지 않았다는 메시지도 나옵니다.
다른 스프링 부트 참고서에는 Security를 어떻게 다루고 있나를 알아보기 위해 알아봤습니다.
먼저 테스트 코드를 통해 "1234"라는 비밀번호를 인코딩하고, 인코딩한 결과값을 다시 원래 코드에 집어넣는 방식으로 실습한 것이 보였습니다.
그리고
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
...
}
원래 소스에는 configureGlobal()메서드로 활용하려고 했는데 여기에서는 configure(AuthenticationManagerBuilder auth)메서드를 재정의하여 또 활용하고 있는 것이 보였습니다.
그래서 현재 실습 중인 Spring Security 5.6.6 버전의 공식문서 내용을 찾아봤습니다.(feat.IntelliJ)
처음에 헤매던 예제와 비교해보면 inMemory를 사용하는 방식이 비슷한 것 같아 configure(AuthenticationManagerBuilder auth)를 재정의하여 구현해봤습니다.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("1234")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("1234")).roles("ADMIN");
}
@Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
id 는 user, password는 1234로 로그인 후 인증에 성공했을 때 화면이 출력됩니다.
구현에 성공했습니다.🎉
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}
원형을 재정의한 후에 안에다가 inMemoryAuthentication()을 통해서 인증 방식을 거치는 것이, 지금 예제에서는 맞다고 판단하고 이를 마치겠습니다.