Spring Security 를 이용해서 UserDetailService 사용하기
이 포스팅은 지난 포스팅에서 이어서 나온 내용입니다.
지난 포스팅에서는 WebSecurityConfig를 만들어서 커스텀 로그인을 만들었습니다.
home과 / 는 로그인을 하지 않아도 접속하게 만드는 것이 저번 포스팅까지 하였습니다.
package com.myBoard.demo;
// import 생략...
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/vendor/**", "/js/**", "/images/**", "/h2-console/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/home", "/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
}
과연 이 다음은 어떻게 될까요?
이 이후엔 Spring Security에서 주어지는 UserDetailService를 이용해서 로그인을 구현합니다.
그걸 위해서는 WebSecurityConfig에 다음을 추가합니다.
package com.myBoard.demo;
// import 생략..
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
@Autowired 어노테이션은 UserDetailService 인터페이스를 implement하는 클래스를 찾아서 주입해 주겠다는 어노테이션 입니다.
자바에서는 하나의 인터페이스 당 하나의 클래스만 implement 할 수 있기 때문에, 어디에 만들든 찾을 수 있습니다. 그럼 userDetailService를 한번 찾아봅시다. 굳이 userDetailService의 이름일 필요는 없습니다. UserDetailService를 implement 하기만 하면 됩니다. 서비스에 대해서는 굉장히 좋은 포스트가 있어서 보면 이해가 훨씬 좋을 것입니다.
onlyformylittlefox.tistory.com/13 ㅁㄴㅇㄹ
package com.myBoard.demo;
// import 생략
import com.myBoard.demo.model.MyUserDetails;
@Service
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new MyUserDetails(username);
}
}
UserDetailService는 의외로 하는일이 별로 없습니다. username이라는 string을 받고, MyUserDetails를 return 하는 것입니다.
그리고 MyUserDetail은 Spring Securtiy에서 나온 interface입니다.
package com.myBoard.demo.model;
// import 생략..
public class MyUserDetails implements UserDetails {
private String userName;
public MyUserDetails(String userName) {
this.userName = userName;
}
public MyUserDetails() {
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public String getPassword() {
return "pass";
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
여기서 잘 보면, getPassword 함수에 하드코딩으로 "pass"를 넣었음을 알 수 있습니다.
결국 우리가 하는 일은, 로그인 화면에서, 아무 아이디를 넣었을 때, 그 myUserDetail이 생성되고 그 암호는 "pass"가 되고 있습니다.
이로, 로그인 화면에서 아무 아이디나 쳐 넣고, 암호에 "pass"라고 넣으면 로그인이 되는 희한한 현상이 보임을 알 수 있습니다.
여기서 두 가지 주의할 점이 있는데, MyUserDetailService 위에 @Service 어노테이션이고, (이거 없으면 안됩니다)
그리고 WebSecurityConfig에서 PassWordEncoder를 오버라이드 해서
return NoOpPasswordEncoder.getInstance(); 를 해야지 됩니다.