http request 에서 custom filter 를 적용하여 특정 header 에 토큰을 담고 해당 토큰이 유효하면 인증된 요청이 되게끔 구현하고 싶었다.
스프링 사이트에서 표시된 이미지다.
솔직히 스프링에 대해서 제대로 모르는 사람이 딱 이 그림을 접했을때 무슨생각이 들지 모르겠다.
위에서 중요한 핵심은 ( custom filter 를 구현할 때 )
1. AuthenticationFilter
2. SecurityContextHolder.getContext() -> Authentication
3. AuthenticationProviders
이 3개다.
security 에 filter 들이 여러개 기본적으로 구현되어 있는데 난 여기에다가 custom filter 를 추가할 계획이다. 그렇다면 어느 위치에 어떻게 넣어줘야 할까?
filter 위치 정하기
spring 에는 기본적으로 config 라는 개념이 있다. 여기에다가 설정해준다.
@EnableWebSecurity
@RequiredArgsConstructor
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// 내가 구현하고 싶은 필터
private final RsaHeaderKeyFilter rsaHeaderKeyFilter;
// 추후 설명될 거임
private final TokenAuthenticationProvider tokenAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/add-not-involve-auth").permitAll()
.anyRequest().authenticated();
// 어느 위치에 추가할 것인지!
http.addFilterBefore(rsaHeaderKeyFilter, BasicAuthenticationFilter.class);
// --> 디버깅 해본 결과 provider 가 두번 타더라 .. 왜그러지 ? 보니
// 해당 프로바이더를 이렇게 설정해줘서 그런거 같아서 지웠더니 한번만 타더라!
//http.authenticationProvider(tokenAuthenticationProvider);
}
}
filter 는 11가지 ? 정도가 구현되어 있는데 내가 구현한 rsaHeaderKeyFilter 는 BasicAuthenticationFilter 전에 추가할거야! 라고 명시해논 config 다.
filter 구현하기
기본적으로 filter 는 request 를 받아서 해당 필터를 할지 말지 말그대로 필터만 하게끔 스프링에서 가이드 해주고 있다.
-> 즉 인증은 filter 에서 하지 않고 다른곳에 '인가' 한다.
말그대로 필터만
@Component
public class RsaHeaderKeyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
if(isContainPaySecurityKey(request)) {
String rsaKey = request.getHeader("token");
RsaAuthToken rsaAuthToken = new RsaAuthToken(rsaKey);
SecurityContextHolder.getContext().setAuthentication(rsaAuthToken);
}
filterChain.doFilter(request,response);
}
private boolean isContainPaySecurityKey(HttpServletRequest request) {
String rsaKey = request.getHeader("test");
return rsaKey != null;
}
}
spring 에서 addFilterBefore 를 사용할 경우 다음과 같이 OncePerRequestFilter 를 extends 해 사용해야 한다.
해당 @Override 를 보게되면 isContainPaySecurityKey() 로 해당 필터를 사용할지 안할지 구분해 주고
해당 필터에서 사용할 인증 객체를 RsaAuthToken 이라고 명명해줬다.
인증객체란 ?
위에 filter 에서 SecurityContextHolder.getContext().setAuthentication(rsaAuthToken); 를 보게 되는데 기본적으로
ContextHolder 에서 인증객체를 다루고 있다.
public class RsaAuthToken extends AbstractAuthenticationToken{
private final String principal;
private Object credentials;
public RsaAuthToken(String principal){
super(null);
this.principal = principal;
this.credentials = null;
}
@Override
public Object getCredentials() {
return credentials;
}
@Override
public Object getPrincipal() {
return principal;
}
}
AbstractAuthenticationToken 을 extends 해서 인증객체를 다룰 수 있다.
해당 인증토큰을 구현한 다른 대표적인 애가 UsernamePasswordAuthenticationToken 인데 해당 객체에서는 흔히
principal 을 id 로 credential 을 password 로 사용하더라
하지만 난 credential 은 딱히 필요가 없다고 생각해서 null 로 두었다.
인증 구현하기
현재 filter 를 만들었고 구현 인증을 위한 객체를 만들었고 filter 를 config 에 넣어서 사용하게 끔 설정했다.
그렇다면 filter 에서 구현 객체를 인증하는 로직은 ?
스프링에서는 filter 따로 인증을 따로 구현하게끔 설정되어 있다. ( 관심사의 분리 역시.. 스프링.. )
인증을 구현하려면 provider 에서 구현해야 한다.
자세한 이미지 보다는 나는 코드랑 디버그로 깨달았다 .. 그리고 이미지를 보니까 이해는 되더라
@Component
public class TokenAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
RsaAuthToken rsaAuthToken = authentication;
// 해당 객체를 true 라고 하면 인증이 된다.
// 여기서 로직을 태워서 만약 인증이 아니라면 throw Authentication 을 던져주면 된다.
rsaAuthToken.setAuthenticated(true);
return rsaAuthToken;
}
@Override
public boolean supports(Class<?> authentication) {
// 인증객체에서 선언한 객체는 reflection 을 통해서 해당 객체인지 파악하는데 이용된다.
return RsaAuthToken.class.isAssignableFrom(authentication);
}
}
자 이제 내부 인증은 세부적으로 구현하면 완성된다!
'개발 > spring' 카테고리의 다른 글
애플리케이션 아키텍처 (0) | 2020.07.19 |
---|---|
스프링이란 무엇인가? (0) | 2020.07.12 |
java spring retrofit2 gson custom serializer 구현 (0) | 2020.05.07 |
spring transaction 이란 (0) | 2020.04.23 |
JPA 애플리케이션 영속성 관리 (0) | 2020.04.13 |
댓글