SecurityConfig

jmjmjames·2022년 5월 12일
0

community-board

목록 보기
1/3

스프링 시큐리티 적용

시큐리티 의존성을 추가합니다.

dependencies {
    ...
    implementation 'org.springframework.boot:spring-boot-starter-security'
    ...

SecurityConfig 클래스

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
  	@Override
    protected void configure(HttpSecurity http) throws Exception {
          http.csrf().disable()   
                  .authorizeRequests()
                      .antMatchers("/", "/auth/**", "/js/**", "/css/**", "/image/**")
                      .permitAll()
                      .anyRequest()
                      .authenticated()
                  .and()
                      .formLogin()
                      .loginPage("/auth/user/login")
                      .loginProcessingUrl("/auth/api/v1/user/login")
                      .defaultSuccessUrl("/");

    }
    
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  • @Configuration: 수동빈 등록

  • @EnableWebSecurity : 스프링 시큐리티 설정들을 활성화

  • csrf().disable() : csrf 토큰 해제

  • authorizeRequests() : URL별 권환 관리를 설정하는 옵션

  • antMatchers() : 권한 관리 대상을 지정하는 옵션
    - "/" 등 지정된 URL들은 permitAll() 열람 권한

  • anyRequest().authenticated(): 다른 요청들은 인증이 되어야함

  • formLogin() : 권한이 없는 유저가 페이지 요청시 → 로그인 페이지로 이동

  • loginPage() : 로그인 페이지

  • loginProcessingUrl() : 스프링 시큐리티가 해당 주소로 요청오는 로그인을 가로채서 대신 로그인

  • defaultSuccessUrl() : 로그인이 성공하면 해당 URL로 이동

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
    ...
    
}
  • enocoder 빈 등록

Security 작동 과정

  1. 시큐리티가 이를 지켜보다가 요청의 아이디와 비밀번호 파라미터를 가로챈다.
  2. 가로챈 정보를 통해 로그인 진행
  3. 시큐리티 세션에 (로그인 유저 정보) 저장
  4. 세션에 저장된 유저 정보를 DI를 통해 가져온다.
  5. 이 때 User 오브젝트를 세션에 저장하지 못하고, UserDetails 오브젝트로 저장한다.

UserDetails 상속

위의 작동 과정에서 User 오브젝트를 세션에 저장하지 못하고, UserDettails를 세션 저장소에 저장 할 수 있습니다.

  • UserDatails 객체를 상속받으면 스프링 시큐리티의 고유한 세션저장소에 저장을 할 수 있게 됩니다.
  • PrincipalDetail 클래스를 생성하여 UserDatails 인터페이스를 상속

PrincipalDetail 클래스

package community.board.config.auth;

import community.board.domain.User;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

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

@Getter
public class PrincipalDetail implements UserDetails {

    private User user;

    public PrincipalDetail(User user) {
        this.user = user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collection = new ArrayList<>();
        collection.add(() -> user.getRoleKey());
        return collection;
    }

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

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

    @Override
    public boolean isAccountNonExpired() {
        return true;    // 계정의 만료 여부(true: 만료되지 않음)
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;    // 계정이 Lock 상태 여부(true: 잠겨있지 않음)
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;    // 패스워드의 만료 여부(true: 만료되지 않음)
    }

    @Override
    public boolean isEnabled() {
        return true;    //계정 활성화 여부 (true: 활성화)
    }
}

PrincipalDetailService 클래스

@RequiredArgsConstructor
@Service
public class PrincipalDetailService implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User principal = userRepository.findByUsername(username).orElseThrow(
                () -> new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다. " + username));
        return new PrincipalDetail(principal);
    }
}
  1. UserDetailService를 통해 username 기반으로 아이디를 찾습니다.
  2. loadUserByUsername 오버라이딩 메서드를 통해 DB에 username의 값을 갖는 user가 있는지 확인
  3. PrincipalDetail(findUser)를 리턴하게 되면 시큐리티 세션에 유저 정보가 저장

SecurityConfig 클래스

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	private final PrincipalDetailService principalDetailService;

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

PrincipalDetailService를 통해 해쉬화된 비밀번호를 DB에 있는 패스워드와 비교 가능합니다.

컨트롤러단에서 세션 정보를 가져올 때

@GetMapping("/auth/user/login")
    public String userLogin(@AuthenticationPrincipal PrincipalDetail principalDetail) {
        return "user/user-login";
    }
profile
Backend Developer

0개의 댓글