[Spring] Spring Security를 이용해 로그인 구현하기

예원·2022년 12월 18일
0

Spring 글 모아보기

목록 보기
13/17
post-custom-banner

스프링 시큐리티란?

Spring Security는 Spring 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크이다.
많은 보안 옵션을 제공해주어 개발자가 보안 로직을 하나씩 작성하지 않아도 되는 장점이 있다.

  • 인증(Authentication) : 해당 사용자가 본인이 맞는지를 확인하는 절차
  • 인가(Authorization) : 인증된 사용자가 요청한 자원에 접근 가능한지 권한을 확인하는 절차

의존성 추가

  • Gradle
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'
  • Maven
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-test</artifactId>
      <scope>test</scope>
</dependency>

Spring Security 구현

Configuration Class 작성

이전에는 WebSecurityConfigurerAdapter를 상속 받아 사용했지만, Spring Security 5.7 이상에서는 이를 지원하지 않아 SecurityFilterChain를 사용하여 구현한다.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

}

@EnableWebSecurity는 WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class들을 import해서 실행 시켜준다. 해당 애노테이션이 있어야 Security를 활성화 시킬 수 있다.

Web Security

WebSecurityCustomizer를 Bean으로 등록하여 필터 적용에 제외 시킬 것을 작성한다.

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        // 정적 자원에 스프링 시큐리티 필터 규칙을 적용하지 않도록 설정
        return (web) -> web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }

HttpSecurity

HttpSecurity는 스프링 시큐리티의 대부분 설정을 담당한다.

  • 리소소(URL) 접근 권한 설정
  • 인증 전체 흐름에 필요한 Login, Logout 페이지 인증완료 후 페이지 인증 실패 시 이동 페이지 등등 설정
  • 인증 로직을 커스텀 하기 위한 커스텀 필터 설정
  • 기타 csrf, 강제 https 호출 등등 거의 모든 스프링 시큐리티의 설정
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                ...
                .and()
                .formLogin()
                ...;

        return http.build();
    }

리소스 접근 권한 설정

.authorizeRequests() 는 리소스 접근 권한을 설정한다.

http
	.authorizeRequests()
	.antMatchers("/", "/register").permitAll()
    .antMatchers("/admin/**").hasAnyRole("ADMIN")
	.anyRequest().authenticated()

andMatchers

  • antMatchers("/", "/register")
  • 특정 리소스에 대해서 권한을 설정한다.

permitAll

  • .antMatchers("/", "/register").permitAll()
  • 설정한 리소스의 접근을 인증 절차 없이 모두 허용한다.

hasAnyRole

  • antMatchers("/admin/**").hasAnyRole("ADMIN")
  • 설정한 리소스의 접근은 설정한 레벨의 권한을 가진 사용자만 허용한다.

anyRequest

  • anyRequest().authenticated()
  • 그 외 나머지 리소스는 인증된 사용자만 접근할 수 있다.

로그인/로그아웃 처리 설정

 http
	.formLogin()
	.loginPage("/login")
	.permitAll()
    .defaultSuccessUrl("/")
    .successHandler(customOAuthSuccessHandler)
    .failureUrl("/login")
    
    .and()
    .logout()
    .logoutSuccessUrl("/")
    .permitAll();

formLogin

  • formLogin()
  • 일반적인 로그인 방식 즉 로그인 페이지와 기타 로그인 처리 및 성공 실패 처리를 사용하겠다는 의미이다.

loginPage

  • loginPage("/login")
  • 사용자가 만든 로그인 페이지를 사용하기 위해 설정한다.
  • 설정하지 않으면 기본 로그인 페이지를 사용한다.

defaultSuccessUrl

  • .defaultSuccessUrl("/")
  • 정상적으로 인증 성공 했을 경우 이동하는 페이지를 설정한다.

successHandler

  • .successHandler(customOAuthSuccessHandler)
  • 정상적 인증 성공 후 별도의 처리가 필요한 경우 커스텀 핸들러를 생성하여 등록할 수 있다.

failureUrl

  • .failureUrl("/login")
  • 인증 실패 했을 경우 이동하는 페이지를 설정한다.

logout

  • logout()
  • 로그아웃 처리를 설정한다.

.logoutSuccessUrl

  • logoutSuccessUrl("/")
  • 정상적으로 로그아웃 했을 경우 이동하는 페이지를 설정한다.

기본 인증 설정

임의의 계정 정보를 지정하여 기본 인증을 진행한다.

configureGlobal

AuthenticationManagerBuilder를 이용해 로그인 인증 처리를 한다.

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
           .withUser("test").password(passwordEncoder().encode("1234"))
           .authorities("ROLE_USER");
}

inMemoryAuthentication

  • auth.inMemoryAuthentication()
  • 스프링 시큐리티가 실행 될 때 사용자를 생성하고, 애플리케이션이 종료되면 삭제하는 사용자이다.
  • 주로 테스트 목적으로 사용하기 적합하다.

withUser.password

  • withUser("test").password(passwordEncoder().encode("1234")).authorities("ROLE_USER")
  • 인증에 사용할 사용자를 추가한다.
  • 이름이 test, 비밀번호가 1234인 사용자를 USER 권한으로 추가하였다.

JDBC 인증 설정

DB에 있는 사용자 정보를 이용해 인증을 진행한다.

configureGlobal

private final DataSource dataSource;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
    throws Exception {
        auth.jdbcAuthentication()
            .dataSource(dataSource)
            .passwordEncoder(passwordEncoder())
            .usersByUsernameQuery("select username, password, enabled "
                + "from user "
                + "where username = ?")
            .authoritiesByUsernameQuery("select u.username, r.authority "
                + "from user_role ur inner join user u on ur.user_id = u.id "
                + "inner join role r on ur.role_id = r.id "
                + "where u.username = ?");
}

jdbcAuthentication

  • auth.jdbcAuthentication()
  • DB 기반의 인증을 처리하도록 한다.

dataSource

  • dataSource(dataSource)
  • dataSource를 지정한다.

usersByUsernameQuery

  • usersByUsernameQuery("select username, password, enabled from user where username = ?")
  • 사용자 이름으로부터 사용자 목록을 가져오는 쿼리를 작성한다.
  • 출력 결과는 사용자이름 , 비밀번호 , enabled 로 총 3개이다.

authoritiesByUsernameQuery

  • authoritiesByUsernameQuery("select u.username, r.authority from user_role ur inner join user u on ur.user_id = u.id inner join role r on ur.role_id = r.id where u.username = ?");
  • 사용자 이름으로부터 사용자의 권한을 가져오는 쿼리를 작성한다.
  • 력 결과는 사용자이름, 권한명 으로 총 2개이다.

passwordEncoder

  • passwordEncoder(passwordEncoder())
  • 패스워드 암호화를 적용한다.
@Bean
public static PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

reference

post-custom-banner

0개의 댓글