반응형
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 |