본문 바로가기
개발/java,spring,springboot

Spring security 기본 설정 - 로그인

by 개발자종혁 2021. 4. 1.
728x90

설정

build.gradle

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

    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
}

코드

WebSecurityConfig.java


import com.fasterxml.jackson.databind.ObjectMapper;

@RequiredArgsConstructor
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final ObjectMapper mapper;

    // 로그인, token 검증하는 provider
    private final JwtLoginAuthenticationProvider jwtAuthenticationProvider;


    private final JwtHeaderAuthenticationFailureHandler jwtHeaderAuthenticationFailureHandler;


    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
        web.ignoring().antMatchers("/static/**");
        web.ignoring().antMatchers("/dist/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // cors 전체 열기 - 테스트용
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin(CorsConfiguration.ALL);
        configuration.addAllowedMethod(CorsConfiguration.ALL);
        configuration.addAllowedHeader(CorsConfiguration.ALL);
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

        http.httpBasic().disable()
            .csrf().disable()
            .cors().configurationSource(source)
            .and().headers().frameOptions();// 

        http.authorizeRequests()
                .antMatchers("/api/**").hasAnyRole("USER")
                .antMatchers("/auth/login").permitAll();

        http.authorizeRequests()
            .and()
                .exceptionHandling().accessDeniedHandler(customAccessDeniedHandler())
            .and()
                .addFilterBefore(jwtAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);

    }


    @Override
    // provider 등록
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(jwtAuthenticationProvider);
    }

    @Bean
    public AccessDeniedHandler customAccessDeniedHandler() {
        return new CustomAccessDeniedHandler(mapper);
    }

    @Bean
    protected JwtLoginAuthenticationProcessingFilter jwtAuthenticationProcessingFilter() throws Exception {
        AntPathRequestMatcher matcher = new AntPathRequestMatcher("/auth/login", HttpMethod.POST.name());
        JwtLoginAuthenticationProcessingFilter filter
                = new JwtLoginAuthenticationProcessingFilter(matcher, jwtAuthenticationSuccessHandler, jwtAuthenticationFailureHandler);
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }


}

JwtLoginAuthenticationProvider.java


import org.springframework.stereotype.Component;

import org.springframework.security.authentication.*;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Component
public class JwtLoginAuthenticationProvider implements AuthenticationProvider {
    private final AdminUserRepository adminUserRepository;
    private final PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
       // authenticate하는 기능 생략 

       return new UsernamePasswordAuthenticationToken("<토큰>", "<비밀번호>", Collections.singletonList(new SimpleGrantedAuthority("USER"))); // "USER"뿐 아니라 다른 ADMIN 등으로 분화하여 나중에 security에서 관리가능

    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }


}

JwtLoginAuthenticationSuccessHandler.java


import org.springframework.stereotype.Component;

import org.springframework.security.authentication.*;
import lombok.RequiredArgsConstructor;

import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

@RequiredArgsConstructor
@Component
public class JwtLoginAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
          // provider에서 아무 exception이 발생하지 않고 정상적으로 진행됐을때 진행되는 곳
    }

}

JwtLoginAuthenticationFailureHandler.java


import org.springframework.stereotype.Component;

import org.springframework.security.authentication.*;
import lombok.RequiredArgsConstructor;

import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

@RequiredArgsConstructor
@Component
public class JwtHeaderAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        // provider에서 exception 발생시에 진행되는 곳
    }

}

JwtLoginAuthenticationProcessingFilter


import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

public class JwtLoginAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
    private final AuthenticationSuccessHandler successHandler;
    private final AuthenticationFailureHandler failureHandler;

    public JwtLoginAuthenticationProcessingFilter(RequestMatcher defaultUrlMatcher, AuthenticationSuccessHandler successHandler, AuthenticationFailureHandler failureHandler) {
        super(defaultUrlMatcher);
        this.successHandler = successHandler;
        this.failureHandler = failureHandler;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        // 이메일, 비밀번호 가져오기 생략
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("<이메일>", "<비밀번호>");
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        successHandler.onAuthenticationSuccess(request, response, authResult);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        failureHandler.onAuthenticationFailure(request, response, failed);
    }
}
728x90

댓글