Spring Security

eunhye_·2023년 3월 15일
0

Spring

목록 보기
3/5
post-custom-banner

스프링 시큐리티란?

Spring 기반의 어플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크이다. Spring Security는 기본적으로 인증 절차를 거친 후에 인가 절차를 진행하며, 인가 과정에서 해당 리소스에 접근 권한이 있는지 확인하게 된다. 기본적으로 쿠키-세션 방식을 사용한다.

인증(Authentication) : 해당 사용자가 본인이 맞는지 확인하는 과정
인가(Authorization) : 해당 사용자가 요청하는 자원을 실행할 수 있는 권한이 있는가를 확인하는 과정

간단한 예시로는 인증은 ID와 PW를 통해 로그인하는 행위, 인가는 인증된 사용자가 어떠한 자원에 접근할 수 있는지를 확인하는 절차.

SecurityFilterChain

스프링 시큐리티를 이용하면 개발시에 필요한 사용자의 인증, 권한, 보안 처리를 간단하지만 강력하게 구현 할 수 있다. 일반적인 웹 환경에서 브라우저가 서버에게 요청을 보내게 되면, DispatcherServlet(FrontController)가 요청을 받기 이전에 많은 ServletFilter(서블릿 필터)거치게 된다. Security와 관련한 서블릿 필터도 실제로는 연결된 여러 필터들로 구성 되어 있다. 이러한 모습때문에 Chain(체인)이라는 표현을 쓴다.

스프링 시큐리티 처리과정

  1. 사용자가 로그인 정보와 함께 인증 요청을 한다.(Http Request)
  2. AuthenticationFilter가 요청을 가로채고, 가로챈 정보를 통해 UsernamePasswordAuthenticationToken의 인증용 객체를 생성한다.(Authentication 인터페이스의 구현체다.)
  3. AuthenticationManager의 구현체인 ProviderManager에게 생성한 UsernamePasswordToken 객체를 전달한다.
  4. AuthenticationManager는 등록된 AuthenticationProvider(들)을 조회하여 인증을 요구한다.
  5. 실제 DB에서 사용자 인증정보를 가져오는 UserDetailsService에 사용자 정보를 넘겨준다.
  6. 넘겨받은 사용자 정보를 통해 DB에서 찾은 사용자 정보인 UserDetails 객체를 만든다.
  7. AuthenticationProvider(들)은 UserDetails를 넘겨받고 사용자 정보를 비교한다.
  8. 인증이 완료되면 권한 등의 사용자 정보를 담은 Authentication 객체를 반환한다.
  9. 다시 최초의 AuthenticationFilter에 Authentication 객체가 반환된다.
  10. Authenticaton 객체를 SecurityContext에 저장한다.

최종적으로 SecurityContextHolder는 세션 영역에 있는 SecurityContext에 Authentication 객체를 저장한다. 그 후 인가 처리를 진행한다.

UserDetails

public class PrincipalDetails implements UserDetails {

    private User user;

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

    //해당 User의 권한을 리턴하는 곳!!
    //인가 처리를 위해 있음.
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collect = new ArrayList<>();
        collect.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return user.getRole();
            }
        });
        return collect;
    } 
    ...
}
  1. 시큐리티가 login 주소요청이 오면 낚아채서 로그인을 진행시킨다
  2. 로그인을 진행이 완료가 되면 시큐리티 session을 만들어준다.(Security ContextHolder)
  3. 시큐리티 세션 오브젝트 안에 Authentication 타입 객체
  4. Authentication 안에 User정보가 있어야 되는데 스프링 시큐리티에서는 User정보를 UserDetails타입 객체로 받는다.

Security Session(Authentication(UserDetails)) 이런 형태가 된다.

UserDetailsService

@Service
public class PrincipalDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    //시큐리티 session(내부 authentication(내부 UserDetaitls))
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
                .orElseThrow(()->
                new UsernameNotFoundException("해당 username이 없습니다"));
        return new PrincipalDetails(user);
    }
}

로그인 처리시 DB에서 실제 유저 정보를 받아 UserDetails객체를 만들어 준다.

인가

@Configuration
@EnableWebSecurity//스프링 시큐리티 필터가 스프링 필터체인에 등록이 됩니다.
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)// secured 어노테이션 활성화, preAuthorized 어노테이션 활성화
public class SecurityConfig {

    @Autowired
    private PrincipalOauth2UserService principalOauth2UserService;
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        http.csrf().disable();
        http.authorizeRequests()
                .antMatchers("/user/**").authenticated()
                .antMatchers("/manager.**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
                .anyRequest().permitAll()
                .and()
                .formLogin()
                .loginPage("/loginForm")
                //.usernameParameter("username2") -> userDetailsService의 loadByUsername함수 파라미터값을 바꾸고싶을때
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/")
        return http.build();
    }
}

스프링 시큐리티 필터에서 인가를 설정할 수 있다.

@Secured("ROLE_ADMIN")
    @GetMapping("/info")
    public @ResponseBody String info(){
        return "개인정보";
    }

    @PreAuthorize("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')")
    @GetMapping("/data")
    public @ResponseBody String data(){
        return "data정보";
    }

Refference
스프링시큐리티_jm3128

post-custom-banner

0개의 댓글