Skip to content

Commit 4741c46

Browse files
committed
refactor(feedback): 인증 필터가 요청당 한 번만 수행되는 것을 보장하도록 OncePerRequestFilter를 상속하도록 변경
- applies feedback next-step#40 (comment)
1 parent ac37cb0 commit 4741c46

File tree

2 files changed

+48
-46
lines changed

2 files changed

+48
-46
lines changed
Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
package nextstep.security.filter;
22

3-
import jakarta.servlet.*;
3+
import jakarta.servlet.FilterChain;
4+
import jakarta.servlet.ServletException;
45
import jakarta.servlet.http.HttpServletRequest;
56
import jakarta.servlet.http.HttpServletResponse;
67
import nextstep.security.AuthenticationException;
78
import nextstep.security.authentication.*;
89
import nextstep.security.util.Base64Convertor;
910
import org.slf4j.Logger;
1011
import org.slf4j.LoggerFactory;
12+
import org.springframework.web.filter.OncePerRequestFilter;
1113

1214
import java.io.IOException;
1315
import java.util.List;
1416

15-
public class BasicAuthenticationFilter implements Filter {
17+
public class BasicAuthenticationFilter extends OncePerRequestFilter {
1618

1719
private static final Logger log = LoggerFactory.getLogger(BasicAuthenticationFilter.class);
1820

@@ -27,37 +29,39 @@ public BasicAuthenticationFilter(UserDetailsService userDetailsService) {
2729
}
2830

2931
@Override
30-
public void doFilter(
31-
ServletRequest servletRequest,
32-
ServletResponse servletResponse,
33-
FilterChain filterChain
34-
) throws IOException, ServletException {
35-
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
36-
37-
if (!AUTHENTICATION_NEED_PATHS.contains(httpRequest.getRequestURI())) {
38-
filterChain.doFilter(servletRequest, servletResponse);
39-
return;
40-
}
41-
42-
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
32+
protected boolean shouldNotFilter(HttpServletRequest request) {
33+
// '/members' 에 대한 요청이 아니면 필터링 하지 말라는 의미. 즉, '/members'에 대한 요청만 필터링
34+
return !AUTHENTICATION_NEED_PATHS.contains(request.getRequestURI());
35+
}
4336

37+
@Override
38+
protected void doFilterInternal(
39+
HttpServletRequest request,
40+
HttpServletResponse response,
41+
FilterChain filterChain
42+
) throws ServletException, IOException {
4443
try {
45-
String authorization = httpRequest.getHeader("Authorization");
46-
String credentials = authorization.split(" ")[1]; // "Basic " 뒤의 문자열
47-
String decodedString = Base64Convertor.decode(credentials);
48-
String[] usernameAndPassword = decodedString.split(":");
49-
String username = usernameAndPassword[0];
50-
String password = usernameAndPassword[1];
44+
Authentication authenticated = attemptAuthentication(request);
45+
request.setAttribute("userDetails", authenticated); // 이후 필터에서 사용 가능하도록 인증된 사용자 정보를 저장
5146

52-
Authentication authRequest = UsernamePasswordAuthenticationToken.unAuthenticated(username, password);
53-
Authentication authenticated = this.authenticationManager.authenticate(authRequest);
54-
55-
httpRequest.setAttribute("userDetails", authenticated); // 이후 필터에서 사용 가능하도록 인증된 사용자 정보를 저장
56-
57-
filterChain.doFilter(servletRequest, servletResponse);
47+
filterChain.doFilter(request, response);
5848
} catch (AuthenticationException | RuntimeException e) {
5949
log.debug("Authentication failed", e);
60-
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
50+
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
6151
}
6252
}
53+
54+
private Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException {
55+
String authorization = request.getHeader("Authorization");
56+
String credentials = authorization.split(" ")[1];
57+
String decodedString = Base64Convertor.decode(credentials);
58+
String[] usernameAndPassword = decodedString.split(":");
59+
60+
Authentication authRequest = UsernamePasswordAuthenticationToken.unAuthenticated(
61+
usernameAndPassword[0],
62+
usernameAndPassword[1]
63+
);
64+
65+
return this.authenticationManager.authenticate(authRequest);
66+
}
6367
}
Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
package nextstep.security.filter;
22

3-
import jakarta.servlet.*;
3+
import jakarta.servlet.FilterChain;
44
import jakarta.servlet.http.HttpServletRequest;
55
import jakarta.servlet.http.HttpServletResponse;
66
import jakarta.servlet.http.HttpSession;
77
import nextstep.security.AuthenticationException;
88
import nextstep.security.authentication.*;
99
import org.springframework.util.StringUtils;
10+
import org.springframework.web.filter.OncePerRequestFilter;
1011

1112
import java.io.IOException;
1213
import java.util.List;
1314
import java.util.Map;
1415

1516
// 인증 정보를 추출하고, AuthenticationManager 에게 비밀번호 맞고 틀리는 것을 검증하는 책임을 위임한다.
16-
public class UsernamePasswordAuthenticationFilter implements Filter {
17+
public class UsernamePasswordAuthenticationFilter extends OncePerRequestFilter {
1718

1819
private static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
1920

@@ -28,37 +29,34 @@ public UsernamePasswordAuthenticationFilter(UserDetailsService userDetailsServic
2829
}
2930

3031
@Override
31-
public void doFilter(
32-
ServletRequest servletRequest,
33-
ServletResponse servletResponse,
34-
FilterChain filterChain
35-
) throws IOException, ServletException {
36-
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
37-
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
38-
39-
if (!AUTHENTICATION_NEED_PATHS.contains(httpRequest.getRequestURI())) {
40-
filterChain.doFilter(servletRequest, servletResponse);
41-
return;
42-
}
32+
protected boolean shouldNotFilter(HttpServletRequest request) {
33+
return !AUTHENTICATION_NEED_PATHS.contains(request.getRequestURI());
34+
}
4335

36+
@Override
37+
protected void doFilterInternal(
38+
HttpServletRequest request,
39+
HttpServletResponse response,
40+
FilterChain filterChain
41+
) throws IOException {
4442
// extract username and password
45-
Map<String, String[]> parameterMap = httpRequest.getParameterMap();
43+
Map<String, String[]> parameterMap = request.getParameterMap();
4644
String username = parameterMap.get("username")[0];
4745
String password = parameterMap.get("password")[0];
4846

4947
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
50-
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "username or password is empty");
48+
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "username or password is empty");
5149
return;
5250
}
5351

5452
Authentication authRequest = UsernamePasswordAuthenticationToken.unAuthenticated(username, password);
5553

5654
try {
5755
Authentication authenticated = this.authenticationManager.authenticate(authRequest);
58-
HttpSession session = httpRequest.getSession();
56+
HttpSession session = request.getSession();
5957
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, authenticated);
6058
} catch (AuthenticationException e) {
61-
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "username or password is empty");
59+
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "username or password is empty");
6260
}
6361
}
6462
}

0 commit comments

Comments
 (0)