본문 바로가기

개발 Note/SPRING

[SPRING] Spring Security

Spring Security란?

엔터프라이즈 애플리케이션에 대한 인증, 권한 부여 및 기타 보안 기능을 제공하는 프레임워크이다. filter 기반으로 동작하고 spring MVC와 분리되어 동작한다.

  • Authenticate(인증) : 현재 사용자가 누구인지 확인
  • Authrize(인가) : 인증된 사용자가 어떤 서비스에 접근할 권한이 있는지 결정
  • Principal(접근 주체) : 보호받는 대상에 접근하는 유저
  • 권한 : 인증된 주체가 애플리케이션의 동작을 수행할 수 있게 허락할지를 결정

[동작 살펴보기]

1. 로그인 요청

브라우저에서 아이디와 비밀번호를 입력하여 로그인을 요청한다.

2. UsernamePasswordAuthenticationToken 발급

AuthenticationFilter로 먼저 요청이 오고, 사용자의 아이디와 비밀번호를 통해 UsernamePasswordAuthenticationToken 을 발급해주어야 한다.

3. Authentication Manager(Interface)에 전달

요청 안의 Authentication을 Authentication Manage에 넘겨주고 Authentication Manager를 구현한 ProviderManager가 처리한다.

  • AuthenticationManager: 인증 요청을 받고 Authentication을 채운다.
  • AuthenticationProvider: 실제 인증이 일어난다.

4. UserDetailService(Interface)에 사용자 아이디 전달

DB에서 인증 정보를 가져올 UserDetailService에 사용자 아이디를 넘겨주고 이를 기반으로 데이터를 조회한다.

5. 인증 시도

AuthenticationProvider가 UserDetails 객체를 전달 받은 이후 실제 사용자의 입력정보와 UserDetails 객체를 가지고 인증을 시도한다.

6. 인증 완료

인증이 완료되면 사용자 정보를 가진 Authentication 객체를 SecurityContextHolder에 담은 후 AuthenticationSuccessHandle를 실행한다.

[Filter 종류]

  • SecurityContextPersistenceFilter : SecurityContextRepository에서 SecurityContext를 로드하고 저장한다.
  • LogoutFilter : 로그아웃 URL로 오는 요청을 감시하고 요청이 있으면 사용자를 로그아웃 시킨다.
  • UsernamePasswordAuthenticationFilter : 폼기반 인증에 사용하는 로그인 URL 요청을 감시하고 요청이 있으면 사용자 인증을 진행한다.
  • DefaultLoginPageGeneratingFilter : 폼기반 또는 OpenID 기반 인증에 사용하는 URL에 대한 요청을 감시한다.
  • BasicAuthenticationFilter : HTTP 기본 인증 헤더를 감시하고 처리한다.
  • RequestCacheAwareFilter : 로그인 성공 이후 원래 요청을 재구성한다.
  • AnonymousAuthenticationFilter : 이 필터가 호출되는 시점까지 사용자 정보가 인증되지 않았다면 인증 토큰에서 익명 사용자로 나타나게 된다.
  • SessionManagementFilter : 인증된 주체를 바탕으로 세션 트래킹을 처리해 단일 주체와 관련한 모든 세션들이 트래킹되도록 도운다.
  • ExceptionTranslationFilter : 보호된 요청을 처리하는 동안 발생할 수 있는 예외의 기본 라우팅과 위임을 처리한다.
  • FilterSecurityInterceptor : AccessDecisionManager 로 권한 부여 처리를 위임함으로써 접근 제어 결정을 쉽게해준다.


[구현하기]

0.세팅


- build.gradle에 추가

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


- WebMvcConfigurer 구현

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
	@Override
    public void addViewControllers(ViewControllerRegistry registry) {
    	//테스트용 요청
    	registry.addViewController("/login").setViewName("login");
        registry.addViewController("/auth").setViewName("auth");
    }
}


- WebSecurityConfig 구현

@Configuration 
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override 
    protected void configure(HttpSecurity http) throws Exception {
    	http.authorizeRequests()
        		// 인증 없이 접근할 수 있는 요청
                .antMatchers("/login").permitAll()
                // auth 요청은 ADMIN role을 가져야 함
                .antMatchers("/auth").hasRole("ADMIN")
                // 나머지 모든 요청에 대해 로그인 요구
                .anyRequest().authenticated()
            .and()
            // 사용자 로그인 화면 설정 
            .formLogin()
            	.loginPage("/login")
                .permitAll()
                .and()
            .logout();
    }
    
    // 패스워드 암호화(bcrypt)
    @Bean
    public BCryptEncoder bCryptEncoder() {
    	return new BCryptPasswordEncoder();
    }
}

1. Model 생성

public class MemVO {
	private String memKey;
    private String email;
    private String password;
    private String authority;
    
    public String getMemKey() {
    	return memkey;
    }
    
    public void setMemKey(String memkey) {
    	this.memkey = memkey;
    }
    
    public String getEmail() {
    	return email;
    }
    
    public void setEmail(String email) {
    	this.email = email;
    }
    
    public String getPassword() {
    	return password;
    }
    
    public void setPassword(String password) {
    	this.password = password;
    }
    
    public String getAuthority() {
    	return authority;
    }
    
    public void setAuthority(String authority) {
    	this.authority = authority;
    }
}

2. Service에서 로그인 처리

@Service 
public class UserServiceImpl implements UserDetailsService { 
	@Autowired
    private UserRepository userRepository; 
    
    @Override 
    public UserDetails loadUserByUsername(String userEmail) throws Exception { 
    	MemVO memVO = repository.findByEmail(userEmail);
        
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority(memVO.getAuthority()));
        return new User(memVO.getEmail(),memVO.getPassword(),authorities);
    }
}


보안은 언제나 어렵다
익숙해지는 수 밖에 없을 듯 싶다😂😂

# References

1] https://docs.spring.io/spring-security/site/docs/4.2.7.RELEASE/reference/htmlsingle/#gradle
2] https://www.javainuse.com/webseries/spring-security-jwt/chap3
3] https://devuna.tistory.com/55
4] https://mangkyu.tistory.com/77