로그인 요청을 할때 Authorization 에다가 HttpBasic 인증 요청을 할때가 있는데 예를들어 다음과 같다.
--header 'Authorization: basic ZWRpeWFPYXV0aDJTZX...'
그런데 만약 Http Basic Authorization 키가 틀릴경우 에러핸들링을 할 수 없을까 해서 살펴보다가 알게되어 여기다가 정리한다.
우선 Basic 인증하도록 HttpSecurity 에 다음처럼 설정한다.
@Configuration
@EnableResourceServer
@RequiredArgsConstructor
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests().anyRequest().authenticated();
}
그런데 이상태에서 Basic 값이 없거나 틀릴경우 다음처럼 에러가 발생한다.
Header 에 Authorization 을 넣지 않는 경우.
틀린 Authorization 값을 갖고 있는 경우(결과값도 없다)
그런데 이 상황에서 문제가 있다는 것을 리턴하기 위해 Exception 핸들링을 하려고 한다. 우선 어디서 필터하는지를 살펴보자.
Spring security 안에 BasicAuthenticationFilter 파일이 있는데 이것은 HttpSecurity 에 .httpBasic() 을 설정해야만 활성화 된다.(위 설정 참고)
BasicAuthenticationFilter.java 파일에 doFilterInternal 에서 수행한다.
...
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request);
if (authRequest == null) {
this.logger.trace("Did not process authentication request since failed to find "
+ "username and password in Basic Authorization header");
chain.doFilter(request, response);
return;
}
String username = authRequest.getName();
this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username));
if (authenticationIsRequired(username)) {
Authentication authResult = this.authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authResult);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
}
this.rememberMeServices.loginSuccess(request, response, authResult);
onSuccessfulAuthentication(request, response, authResult);
}
}
catch (AuthenticationException ex) {
SecurityContextHolder.clearContext();
this.logger.debug("Failed to process authentication request", ex);
this.rememberMeServices.loginFail(request, response);
onUnsuccessfulAuthentication(request, response, ex);
if (this.ignoreFailure) {
chain.doFilter(request, response);
}
else {
this.authenticationEntryPoint.commence(request, response, ex);
}
return;
}
chain.doFilter(request, response);
}
...
여기서 Basic 값이 올바르지 않으면 AuthenticationException 으로 빠지게 되는데, this.ignoreFailure 를 설정하지 않았기 때문에(기본값 false) authenticationEntryPoint.commence를 호출하고 종료된다.
그래서 저 부분을 따로 구현해서 붙여주면 Exception 핸들링이 가능하다.
다음 파일을 생성하자.
public class CustomBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
// super.commence(request, response, authException);
Map responseMap = new HashMap();
responseMap.put("success", false);
responseMap.put("resultMsg", "인증 실패했습니다");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
String json = new Gson().toJson(responseMap);
response.getWriter().println(json);
}
@Override
public void afterPropertiesSet() {
setRealmName("myproject");
super.afterPropertiesSet();
}
}
commence 에다 리턴할 시에 필요한 데이터를 구성한 뒤 response에 넣었다.
(여기서는 Gson을 이용해 json으로 파싱해서 리턴하도록 했다.)
또한 setRealmName 설정을 반드시 해줘야 하는데, 해당 이름은 이 필터가 적용되는 공간의 이름을 지정하는 것이다. 관련된 내용은 다음 링크에서 확인하자.
https://stackoverflow.com/questions/12701085/what-is-the-realm-in-basic-authentication
이것을 HttpSecurity 에 적용해야 한다. 다음처럼 수정하도록 하자.
@Configuration
@EnableResourceServer
@RequiredArgsConstructor
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.httpBasic()
// 아래 추가
.authenticationEntryPoint(new CustomBasicAuthenticationEntryPoint())
.and()
.authorizeRequests().anyRequest().authenticated();
}
이렇게 한 뒤 Basic 에 들어가는 값을 일부로 틀리게 해보자.
설정한대로 값이 내려오는 것을 확인할 수 있다.
끝.
'공부 > 프로그래밍' 카테고리의 다른 글
[aws, sqs, springboot] fifo 전송 에러 (A queue already exists with the same name and a different ...) (0) | 2021.09.22 |
---|---|
[springboot] RestTemplate 를 사용할 때 401 에 body가 없는 경우(no body) (0) | 2021.08.06 |
[React, PWA] 클라이언트에서 웹 푸시(fcm) 설정하기 (0) | 2021.07.28 |
[springboot] feign 설정하기 (0) | 2021.07.26 |
[spring security, jwt] jwt 인증 설정하기 + Cannot convert access token to JSON 에러 잡기 (0) | 2021.07.19 |
댓글