Spring Security Form Login - 2. Security 설정 및 UserDetailsService 정의하기

  1. 로그인 로그아웃을 처리를 위한 Controller 생성
  2. Spring Security 설정하기
  3. UserDetailsService 구현하기

로그인 로그아웃을 처리를 위한 Controller

/login으로 사용자 로그인 요청을 처리하고 /logout으로 사용자 로그아웃 요청을 처리한다.

@Controller
@RequiredArgsConstructor
public class SecurityController {
private final CustomUserDetailsService customUserDetailsService;
private final PasswordEncoder passwordEncoder;

@GetMapping("/")
public String index(){
return "index";
}

@GetMapping("/login")
public String login(){
return "login";
}

@GetMapping("/logout")
public void logout(){
}
}

Spring Security 설정하기

Spring Security를 설정하고 싶으면 EnableWebSecurity어노테이션과 WebSecurityConfigurerAdapter 클래스를 상속해 method들을 overriding한다.

  • configure(AuthenticationManagerBuilder auth)
    • AuthenticationManager를 설정하기 위해 사용하는 method
  • configure(WebSecurity web)
    • 전역적인 security를 설정할 때 사용하는 method, 보통은 보안 예외처리에 사용한다.
  • configure(HttpSecurity http)
    • request 단위로 보안을 걸때 사용하는 method

configure(HttpSecurity http)을 이용해 허가를 하는 경우에는 Spring Filter Chain을 거처야 하지만 configure(WebSecurity web)를 이용하면 Spring Filter Chain을 거치지 않는다.

@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomUserDetailsService customUserDetailsService;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}

@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/css/**", "/js/**")
.antMatchers("/favicon.ico", "/resources/**", "/error")
;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.anyRequest().authenticated();


http
.formLogin() // Form Login을 이용한 인증 방식을 사용
.loginPage("/login") // Login Page로 redirect할 경로를 적어준다.
.defaultSuccessUrl("/", true) // 로그인 성공후 이동할 경로
.permitAll()
;
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

UserDetailsService 등록하기

AuthenticationManagerBuilder객체를 이용해 인증시 사용하는 UserDetailsService를 지정해준다.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}

정적파일을 Security Filter에서 제외 시키기

css파일이나 js파일에 대한 예외처리를 하지 않는 경우 화면을 그릴때 해당 자원에 대한 접근을 하지 못하므로 화면이 깨져 나오게 된다.

@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/css/**", "/js/**")
.antMatchers("/favicon.ico", "/resources/**", "/error")
;
}

요청에 따라서 인증/인가를 설정

로그인을 하기 위한 login페이지에 대한 접근은 허용하고, 그 외의 모든 자원에 대한 요청은 인증을 요구하도록 한다.

@Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.anyRequest().authenticated();


http
.formLogin() // Form Login을 이용한 인증 방식을 사용
.loginPage("/login") // Login Page로 redirect할 경로를 적어준다.
.defaultSuccessUrl("/", true) // 로그인 성공후 이동할 경로
.permitAll()
;
}

Service생성하기

Spring Security는 인증 과정에서 UserDetails를 구현한 객체를 이용한다. 따라서 Service를 생성하기 위해서는 UserDetailsService를 이용해 Service를 만든다.

UserDetialsService 인터페이스는 loadUserByUsername 메서드가 있는데, username을 이용해 DB로부터 사용자 정보를 얻어와 UserDetails를 반환하는 메서드이다.

@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final AccountRepository accountRepository;

@Autowired
private PasswordEncoder passwordEncoder;

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
// return new User("user", passwordEncoder.encode( "1234"), new ArrayList<>());
Optional<Account> optional = accountRepository.findByEmail(email);

if(optional.isPresent()){
Account account = optional.get();
return new SecurityAccount(account);
}else{
throw new UsernameNotFoundException(email + "사용자 없음");
}
}

public void saveAccount(Account account){
accountRepository.save(account);
}
}

DB로부터 User정보를 가져와 UserDetails객체로 반환

UserDetailsService인터페이스를 구현하면서
username을 매게변수로 받는 loadUserByUsername메소드를 Overriding해,
DB에 접근해 User정보를 가져오고 해당 객체를 UserDetails객체를 반환하도록 Overriding 해준다.

loadUserByUsername메소드를 Overriding 해줘야 한다. loadUserByUsername메소드는 username을 매게변수로 받아 username을 이용해 DB로부터 User정보를 가져오고 해당 객체를 UserDetails객체로 반환하도록 Overriding 해준다.

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
// return new User("user", passwordEncoder.encode( "1234"), new ArrayList<>());
Optional<Account> optional = accountRepository.findByEmail(email);

if(optional.isPresent()){
Account account = optional.get();
return new SecurityAccount(account);
}else{
throw new UsernameNotFoundException(email + "사용자 없음");
}
}

User객체 생성하기

Spring Security는 인증시 UserDetails객체를 사용하므로 Account객체를 UserDetails객체로 변환해줘야 한다.

Spring Security에서는 UserDetails를 객체를 쉽게 만들 수 있도록 UserDetails를 구현한 User객체를 지원한다.

public class SecurityAccount extends User {
public SecurityAccount(Account account){
super(account.getEmail()
, account.getPassword()
, AuthorityUtils.createAuthorityList(account.getRole().getValue()));
}
}
Share