Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spring Security

ChanGrea
August 31, 2020

Spring Security

Spring Security의 개요, 구조, 인증/인가에 대해서 세미나 했던 자료

ChanGrea

August 31, 2020
Tweet

More Decks by ChanGrea

Other Decks in Programming

Transcript

  1. Spring? u Java 기반 OpenSource Framework u 동적인 웹 사이트

    개발하기 위한 여러 가지 서비스 제공 u 다양한 프로젝트 u Spring Boot : Spring의 설정을 최소화한 것 u Spring Data : 데이터베이스 접근을 위한 일관되고 추상화된 모델 제공 u Spring Cloud : 분산 시스템의 공통적인 패턴을 모아 제공 u Spring MVC : Model-View-Controller 기반의 웹 프레임워크 u Spring Security : Spring Application의 보안(인증과 권한 등)을 담당
  2. Spring Security u Spring Application의 보안(인증과 권한 등)을 담당 u

    기본적인 보안 기능 u 인증(Authentication)과 인가(Authorization) u 인증(Authentication) ⇨ “이 앱의 사용자가 맞는가?” (Who?) u 인가(Authorization) ⇨ “그 사용자가 리소스를 다룰 수 있는가?” (Can?) u 강화된 보안 기능 u 세션 관리 u CSRF 방지 u 브라우저의 보안 기능과의 연계 u …
  3. Spring Security 구조(Architecture) 1. 클라이언트는 웹 애플리케이션에 요청을 보낸다. 2.

    FilterChainProxy 클래스(서블릿 필터)가 요청을 받고, HttpFirewall 메서드를 호출해서 HttpServletRequest와 HttpServletResponse에 대한 방화벽 기능 수행 3. SecurityFilterChain에 설정돼 있는 보안 필터 클래스에 처리 위임 4. SecurityFilterChain은 여러 보안 필터가 연쇄적으로 연결된 형태. 앞 필터 처리 후, 뒤 필터가 뒤이어 호출. 5. 마지막 보안 필터 처리 후, 남은 서블릿 필터나 서블릿이 실행되어 웹 애플리케이션의 리소스에 접근 6. FilterChainProxy 클래스는 웹 애플리케이션에서 반환한 리소스를 클라이언트에 전달
  4. Spring Security 설정 (dependency) u spring-security-web u spring-security-config u pom.xml

    <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> </dependency>\
  5. Spring Security 설정 (Bean 설정) u XML 파일에 설정하기도 하지만,

    Java 기반으로 설정한다. import org.springframework.security.config.annotation.web.builders.*; import org.springframework.security.config.annotation.web.configuration.*; import org.springframework.context.annotation.Configuration; @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) { web.ignoring().antMatchers("/resources/**"); //js나 css는 보안 기능이 필요없으므로 제외 } }
  6. 인증 처리 u 애플리케이션을 이용하는 사용자의 정당성을 확인하기 위한 기능

    u 사용자 정보를 데이터 저장소에 등록해 두고, 입력 정보와 비교하는 방법 u HTML 입력 폼을 사용하는 방식 u RFC에서 정해진 HTTP 표준 인증 방식(Basic 인증, Digest 인증 등)을 이용 u OpenID 인증이나 Single Sign On 인증 같은 인증 방식 이용
  7. 인증 처리 메커니즘 1. 자격정보(사용자명과 패스워드)를 요청 파라미터로 요청 2.인증

    필터(Authentication Filter)는 요청 파라미터에서 자격정보를 구한 다음, AuthenticationManager 클래스의 인증 메서드를 호출한다. 3.ProviderManager(AuthenticationManager의 기본 구현 클래스)는 실제 인증 처리를 AuthenticationProvider 인터페이스의 구현 클래스에 위임한다.
  8. 인증 처리 방식에 대한 구현을 제공하는 서블릿 필터 인증 처리를

    수행하기 위한 인터페이스 인증 처리 기능을 구현하기 위한 인터페이스
  9. Form 인증 1.클라이언트는 폼 인증이 필요한 리소스 경로에 접근할 때

    자격정보(사용자명과 패스워드)를 요청 파라미터로 전송한다. 2.UsernamePasswordAuthenticationFilter 클래스는 요청 파라미터에서 자격정보를 구한 다음, AuthenticationManager를 통해 해당 이용자가 인증된 사용자인지 확인한다. 3.인증에 성공한 경우 AuthenticationSuccessHandler 메서드를 호출, 실패한 경우 AuthenticationFailureHandler 메서드를 호출해서 화면을 이동시킨다.
  10. Form 인증 설정 u 자바 기반 설정 @EnableWebSecurity public class

    WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 생략 http.formLogin(); } } 설정만 잘 했다면, ‘/login’ 경로로 GET 요청 시, 기본 로그인 폼 표시되고, 로그인 버튼을 누르면 POST 요청으로 인증 처리가 수행됨
  11. Form 인증 설정 (Customizing) u 기본적으로 제공하는 로그인 폼을 그대로

    사용하는 경우는 거의 없다.. u 직접 만든 로그인 폼이 있다고 가정하고 적용하는 방법? u “src/main/webapp/views/” 아래에 loginForm.jsp라는 로그인 폼 파일이 이미 있다고 가정 u 로그인 폼을 표시하기 위한 Controller @Controller public class AuthenticationController { @RequestMapping(path = "/login", method = RequestMethod.GET) public String viewLoginForm() { return "loginForm"; } }
  12. Form 인증 설정 (Customizing) u 로그인 폼을 스프링 시큐리티에 적용하기

    위한 빈 정의 u '/authenticate' 경로로 uid(사용자명), pwd(패스워드) 파라미터르 요청 @Override protected void configure(HttpSecurity http) throws Exception { // 생략 http.formLogin() .loginProcessingUrl("/authenticate") .usernameParameter("uid") .passwordParameter("pwd") .permitAll(); http.authorizeRequests() .anyRequest() .authenticated(); }
  13. Form 인증 설정 (Customizing) u 인증에 성공했을 때? u 인증에

    실패했을 때? @Override protected void configure(HttpSecurity http) throws Exception { // 생략 http.formLogin() .defaultSuccessUrl("/menu") .permitAll(); } @Override protected void configure(HttpSecurity http) throws Exception { // 생략 http.formLogin() .failureUrl("/loginFailure") .permitAll(); }
  14. 데이터베이스 인증 DaoAuthenticationProvider에 인증 처리를 위임 UserDetailsService에게 사용자 정보를 가져오게

    한다. 데이터 저장소에서 사용자 정보를 가져온다. 가져온 사용자 정보를 사용해 UserDetails를 만든다.
  15. 데이터베이스 인증 u UserDetails 구현 u UserDetails는 인증 처리에 필요한

    자격정보(사용자명과 패스워드)와 사용자의 상태 정보를 제공하기 위한 인터페이스로서 다음과 같은 메서드가 정의돼 있다. public interface UserDetails extends Serializable { String getUsername(); // 사용자명 반환 String getPassword(); // 등록된 패스워드 반환(패스워드 틀리면 BadCredentialsException 발생) boolean isEnabled(); // 유효한 패스워드인지 판단 boolean isAccountNonLocked(); // 계정의 잠금 상태를 판단 boolean isAccountNonExpired(); // 계정의 유효 기간 상태를 판단 boolean isCredentialsNonExpired(); // 자격정보의 유효 기간 상태를 판단 Collection<? extends GrantedAuthority> getAuthorities(); // 사용자가 가진 권한 리스트 반환 (인가 처리를 할 때 필요) }
  16. 데이터베이스 인증 u 스프링 시큐리티는 UserDetails의 구현 클래스로 User 클래스를

    제공한다. public class AccountDetails extends User { private final Account account; public AccountUserDetails(Account account, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<GrantedAuthority> authorities) { super(account.getUsername(), account.getPassword(), account.isEnabled(), true, true, true, authorities); this.account = account; } public Account getAccount() { return account; } }
  17. 데이터베이스 인증 u UserDetailsService 작성 u 자격정보와 사용자 상태 정보를

    데이터 저장소에서 가져오기 위한 인터페이스 public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
  18. 데이터베이스 인증 u UserDetailsService 인터페이스를 구현한 예 @Service public class

    AccountUserDetailsService implements UserDetailsService { @Autowired AccountRepository accountRepository; @Transactional(readOnly = true) public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Account account = Optional .ofNullable(accountRepository.findOne(username)) .orElseThrow(() -> new UsernameNotFoundException("user not found.")); return new AccountUserDetails(account, getAuthorities(account)); } private Collection<GrantedAuthority> getAuthorities(Account account) { if(account.isAdmin()) { return AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_USER", "ROLE_ADMIN"); } else { return AuthorityUtils.createAuthorityList("ROLE_USER"); } } } 참고로 스프링 시큐리티에서 인가 처리를 할 때는 ‘ROLE_’로 시작하는 권한 정보를 롤로 취급한다.
  19. 데이터베이스 인증 u 앞서 만든 UserDetailsService를 사용해 사용자 인증 처리를

    하려면AuthenticationManagerBuilder에 UserDetailsService를 적용해야 한다 @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserDetailsService userDetailsService; @Autowired void configureAuthenticationManager(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder()); } @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
  20. 로그아웃 처리 1.클라이언트는 로그아웃을 처리하는 경로로 요청을 보낸다. 2.LogoutFilter는 LogoutHandler의

    메서드를 호출해서 로그아웃 처리를 한다. 3.LogoutFilter는 LogoutSuccessHandler의 메서드를 호출해서 화면을 이동한다. 스프링 시큐리티가 제공하는 빈이 LogoutFilter에 자동으로 설정되기 때문에 직접 구현할 필요는 없다.
  21. 로그아웃 처리(적용) u 빈 정의를 해줌으로써 적용이 완료된다. u 성공했을

    때 처리는 아래와 같이 해준다. @Override protected void configure(HttpSecurity http) throws Exception { // 생략 http.logout() .logoutUrl("/auth/logout") .permitAll(); } @Override protected void configure(HttpSecurity http) throws Exception { // 생략 http.logout() .logoutSuccessUrl("/logoutSuccess") .permitAll(); //생략 }
  22. 인가 처리 u 애플리케이션에서 사용자가 접근할 수 있는 리소스를 제어하기

    위한 기능 u 각 리소스에 대한 접근 정책을 미리 정의, 접근 시 정책 확인 해서 허용 여부 결정 u 웹 리소스, 자바 메서드, 도메인 객체에 대한 접근 정책 정의 가능
  23. 인가 처리 구조 1. 클라이언트가 임의의 리소스에 접근한다. 2. FilterSecurityInterceptor

    클래스는 AccessDecisionManager 인터페이스의 메서드를 호출하고 리소스에 대한 접근 가능 여부를 확인한다. 3. AffirmativeBased 클래스는 AccessDecisionVoter 인터페이스의 메서드를 호출하고 접근 가능 여부에 대한 투표 결과를 받는다. 4. FilterSecurityInterceptor는 AccessDecisionManager가 허용할 때만 리소스의 접근을 허락한다.
  24. 접근 정책을 기술하는 방법 u 스프링 표현 언어(SpEL) 사용 u

    공통 표현식 표현식 설명 hasRole(String role) 해당 롤을 가지고 있는 경우 true hasAnyRole(String… roles) 해당 롤 중에 하나를 가지고 있는 경우 true isAnonymous() 익명 사용자인 경우 true isRememberMe() Remember Me 인증을 통해 로그인한 경우 true isAuthenticated() 이미 인증된 사용자인 경우 true isFullyAuthenticated() Remember Me가 아닌 일반적인 인증 방법으로 로그인한 경우 true permitAll 항상 true denyAll 항상 false principal 인증된 사용자의 사용자 정보(UserDetails 구현한 클래스의 객체) 반환 authentication 인증된 사용자의 인증 정보(Authentication 구현한 클래스의 객체) 반환
  25. 접근 정책을 기술하는 방법 u 웹 표현식 u 웹 리소스에

    대한 인가(자바 기반 설정) u 접근 정책을 적용할 웹 리소스를 지정한다. 표현식 설명 hasIpAddress(String ipAddress) 인수에 지정한 IP 주소 체계에 클라이언트의 IP 주소가 일치하는 경우에 true 반환 표현식 설명 antMatchers ant 형식으로 지정한 경로 패턴과 일치하는 리소스를 적용 대상 regexMatchers 정규 표현식으로 지정한 경로 패턴과 일치하는 리소스를 적용 대상 requestMatchers 지정한 RequestMatcher 인터페이스 구현과 일치하는 리소스를 적용 대상 anyRequest 기타 리소스를 적용 대상
  26. 접근 정책에 대한 몇가지 예시 @Override protected void configure(HttpSecurity http)

    throws Exception { // 생략 http.authorizeRequests() .antMatchers("/admin/accounts/**").hasRole("ACCOUNT_MANAGER") .antMatchers("/admin/configurations/**") .access("hasIpAddress('127.0.0.1') and hasRole('CONFIGURATION_MANAGER')") .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated(); } @Override protected void configure(HttpSecurity http) throws Exception { // 생략 http.authorizeRequests() .antMatchers("/users/{username}") .access("isAuthenticated() and (hasRole('ADMIN') or (#username == principal.username))") .anyRequest().authenticated(); }
  27. CSRF 방지 u CSRF(크로스 사이트 요청 변조) u 사용자의 의지와는

    무관한 수정/삭제/등록 등의 행위를 요청하는 것 u Spring Security는 아래와 같은 방법으로 CSRF를 방지 u 세션 단위로 무작위로 만든 토큰 값(CSRF 토큰)을 발급 u 그 토큰을 요청 파라미터(HTML 폼의 hidden 항목)에 포함시켜서 전송
  28. CSRF 방지 기능의 적용 u Spring Framework 3.2부터 추가됨 u

    4.0부터는 기본 적용되는 기능 u 따로 뭔가를 정의할 필요가 없다 u 하지만 CSRF 방지 기능을 적용하고 싶지 않을 때는 명시적으로 비활성해야 한다. @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); }