Back/Spring Java

Spring Security Login

밍꿔 2019. 12. 18. 17:04


반응형

 

Spring Security를 사용하기 전에 로그인 로직을 만드려면 이래저래 고려해야하고 예외처리 해

 

야할 부부들에 대해서 고민해야 할 내용이 많았다. 하지만 Spring Security를 사용하여 구성하게

 

되면 비교적 간단히 개발이 가능하다.

 

 

Security Config

@Configuration
@EnableWebSecurity
@EnableGlobalAuthentication
@ComponentScan(basePackages = {"com.example.study.*"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    AuthProvider authProvider;
   
    @Autowired
    AuthFailureHandler authFailureHandler;
    
    @Autowired
    AuthSuccessHandler authSuccessHandler;
   
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 허용되어야 할 경로들
        web.ignoring().antMatchers(
                "/resources/**"
                ,"/lib/**"
                ,"/app/**"
                ,"/common/**"
                                    ); 
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 로그인 설정
        http	.authorizeRequests()
                // ROLE_USER, ROLE_ADMIN으로 권한 분리 유알엘 정의
                .antMatchers("/", "/pages/login", "/api**", "/error**").permitAll()
                .antMatchers("/**").access("ROLE_USER")
                .antMatchers("/**").access("ROLE_ADMIN")
                //.antMatchers("/admin/**").access("ROLE_ADMIN")
                .antMatchers("/**").authenticated()
                .and()
                // 로그인 페이지 및 성공 url, handler 그리고 로그인 시 사용되는 id, password 파라미터 정의
                .formLogin()
                .loginPage("/pages/login")          // 로그인 페이지 
                .defaultSuccessUrl("/")
                .failureHandler(authFailureHandler) // 로그인 실패 시 진행될 Handler
                .successHandler(authSuccessHandler) // 로그인 성공 시 진행될 Handler
                .usernameParameter("username")      // 화면에서 받아오는 ID 파라미터 명
                .passwordParameter("password")      // 화면에서 받아오는 PW 파라미터 명
                .and()
                // 로그아웃 관련 설정
                .logout().logoutRequestMatcher(new AntPathRequestMatcher("/pages/logout"))
                .logoutSuccessUrl("/")				// 로그인 성공 후 접근 Url
                .invalidateHttpSession(true)
                .and()
                // csrf 사용유무 설정
                // csrf 설정을 사용하면 모든 request에 csrf 값을 함께 전달해야한다.
                .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .and()
                // 로그인 프로세스가 진행될 provider
                .authenticationProvider(authProvider);
    }

}

 

 

AuthProvider

/**
 * 인증 프로바이더
 * 로그인시 사용자가 입력한 아이디와 비밀번호를 확인하고 해당 권한을 주는 클래스
 */
@Component("authProvider")
public class AuthProvider implements AuthenticationProvider  {

    @Autowired
    UserService userService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String id = authentication.getName();
        /*String password = HashUtil.sha256(authentication.getCredentials().toString()); todo*/
        String password = String.valueOf(authentication.getCredentials());

        UserDetails user = userService.loadUserByUsername(id);

		// 이부분은 custom하게 구성하여 예외처리 진행
        if (null == user){
            throw new UsernameNotFoundException(id);
        }else if(!user.getPassword().equals(password)){
            throw new BadCredentialsException(id);
        }else if(!user.isAccountNonLocked()){
            throw new LockedException(id);
        }else if(!user.isEnabled()){
            throw new DisabledException(id);
        }else if(!user.isAccountNonExpired()){
            throw new AccountExpiredException(id);
        }else if(!user.isCredentialsNonExpired()){
            throw new CredentialsExpiredException(id);
        }

        List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();

		// 로그인 인증 토큰
        UsernamePasswordAuthenticationToken token = 
        	new UsernamePasswordAuthenticationToken(id, password, user.getAuthorities());
        token.setDetails(user);
        return token;
    }

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

}

 

 

AuthSuccessHandler

/**
 * 로그인 성공 핸들러
 */
@Component
public class AuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws ServletException, IOException {
		
        //Http Response Code Set
        response.setStatus(HttpServletResponse.SC_OK);

        // 성공 시 response를 json형태로 반환
        //response.getWriter().print("{\"success\": true}");
        //response.getWriter().flush();
        
        // main redirect
        response.sendRedirect("/pages");
    }

}

 

 

AuthFailureHandler

/**
 * 로그인 실패 핸들러
 */
@Component
public class AuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                                AuthenticationException exception) throws IOException, ServletException {

        String errorCode = "";  // 에러 코드
        String errorMsg = "";   // 에러 메세지

        if(exception instanceof AuthenticationServiceException){    // 인증 요구가 거부됐을 때 던지는 예외
            errorCode = LoginFailType.AUTH_FAIL.getId();
            errorMsg = LoginFailType.AUTH_FAIL.getMsg();
        }
        if(exception instanceof BadCredentialsException){           // 비밀번호가 일치하지 않을 때 던지는 예외
            errorCode = LoginFailType.BAD_CREDENTIALS.getId();
            errorMsg = LoginFailType.BAD_CREDENTIALS.getMsg();
        }
        if(exception instanceof LockedException){                   // 인증 거부 - 잠긴 계정
            errorCode = LoginFailType.LOCKED_ACCOUNT.getId();
            errorMsg = LoginFailType.LOCKED_ACCOUNT.getMsg();
        }
        if(exception instanceof DisabledException){                 // 인증 거부 - 계정 비활성화
            errorCode = LoginFailType.DISABLED_ACCOUNT.getId();
            errorMsg = LoginFailType.DISABLED_ACCOUNT.getMsg();
        }
        if(exception instanceof AccountExpiredException){           // 인증 거부 - 계정 유효기간 만료
            errorCode = LoginFailType.EXPIRED_ACCOUNT.getId();
            errorMsg = LoginFailType.EXPIRED_ACCOUNT.getMsg();
        }
        if(exception instanceof CredentialsExpiredException){       // 인증 거부 - 비밀번호 유효기간 만료
            errorCode = LoginFailType.EXPIRED_PASSWORD.getId();
            errorMsg = LoginFailType.EXPIRED_PASSWORD.getMsg();
        }
        if(exception instanceof UsernameNotFoundException){         // 계정정보가 없을때
            errorCode = LoginFailType.NONE_ACCOUNT.getId();
            errorMsg = LoginFailType.NONE_ACCOUNT.getMsg();
        }

        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);    // 인증거부
        response.getWriter().print("{\"errorCode\": \""+errorCode+"\", \"errorMsg\": \""+errorMsg+"\"}");
        response.getWriter().flush();
        //response.sendRedirect("/pages/login?error="+errorCode);
    }
}
반응형

'Back > Spring Java' 카테고리의 다른 글

BeanUtils.copyProperties  (0) 2019.12.23
Blob형태의 이미지 파일 입출력  (0) 2019.12.19
가변인자 Varargs  (0) 2019.12.18
제네릭(Generic) 문법  (0) 2019.11.25
Lombok - @Slf4j  (0) 2019.11.25