인프런 커뮤니티 질문&답변

Backend.dev님의 프로필 이미지
Backend.dev

작성한 질문수

스프링 시큐리티

안녕하세요. 선생님 강의를 보고 현재 OAuth2 + JWT 인증 인가를 구현 중인데... 질문을 드려도 될까요?

작성

·

772

0

안녕하세요 선생님.

선생님의 이론 강의를 다 보고나서 시큐리티로 OAuth2.0 + JWT 인증 / 인가 를 구현하고 있는데요.

계속 생각처럼 작동을 안하고 있는데 도무지 어디가 잘 못 된건지 잘 모르겠습니다.

혹시 이메일 같은 곳으로(개인적인 프로젝트라 공개적으로 이 곳에 올리긴 힘들거 같아서요...^^;) 소스 코드를 공유 해드린다면 잘못된 것들을 좀 알려주실 수 있을까요?

정답까지는 알려주시지 않더라도, 어디가 잘못 된건지 (구현할 수 없는걸 구현하려 하고 있지는 않은지) 만 알려주셔도 굉장히 큰 도움이 될거 같습니다ㅠㅠ

혹시 부탁을 드려도 될까요?

답변 6

1

정수원님의 프로필 이미지
정수원
지식공유자

전반적인 흐름 및 구성자체는 괜찮은 것 같습니다. 

OAuth2 인증 이후 DB 에 저장하고 OAuthAccount에 Account 정보를 담아 리턴하면 내부적으로 스프링 시큐리티의  OAuth2LoginAuthenticationProvider 가 OAuth2LoginAuthenticationToken 을 생성하고 여기에 OAuthAccount 및 권한 등의 여러가지 정보를 담아 다시 리턴하면 최종적으로 OAuth2LoginAuthenticationFilter 가 OAuth2AuthenticationToken 를 생성한 다음 이 객체에 리턴 받은 OAuth2LoginAuthenticationToken 의 정보를 저장하고 있습니다.

OAuth2LoginAuthenticationProvider

OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
loginAuthenticationToken.getClientRegistration(),
loginAuthenticationToken.getAuthorizationExchange(),
oauth2User,
mappedAuthorities,
accessToken,
authorizationCodeAuthenticationToken.getRefreshToken());
authenticationResult.setDetails(loginAuthenticationToken.getDetails());

return authenticationResult;
OAuth2LoginAuthenticationFilter

OAuth2LoginAuthenticationToken authenticationResult =
(OAuth2LoginAuthenticationToken) this.getAuthenticationManager().authenticate(authenticationRequest);

OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(
authenticationResult.getPrincipal(),
authenticationResult.getAuthorities(),
authenticationResult.getClientRegistration().getRegistrationId());
oauth2Authentication.setDetails(authenticationDetails);

OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
authenticationResult.getClientRegistration(),
oauth2Authentication.getName(),
authenticationResult.getAccessToken(),
authenticationResult.getRefreshToken());

this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);

return oauth2Authentication;

그리고 이것을 SecurityContext 에 저장합니다.

그런 다음 CustomAuthenticationSuccessHandler 에서 임시토큰을 만들어 클라이언트로 보내고 다시 /token 으로 리다이렉트 하여 검증 과정을 거친 후 jwt 토큰을 발급받는 것 같습니다. 

이 과정에서 /token 요청이 JwtAuthenticationFilter 를 거치게 되어있는데 이 필터는 헤더에 토큰값이 없을 경우 예외가 발생하도록 되어 있어서 서버로부터 jwt token 을 발급받지 못하는 것 같습니다. 

그렇다면 JwtAuthenticationFilter 는 /token 요청시엔 그냥 패스하도록 조건을 주거나 web.ignoring 을 사용해서 필터링을 거치지 않도록 해야 할 것 같습니다.  

헤더에 토큰을 가지고 요청할 경우 다음구문에서 jwt 검증을 거치게 되고 검증이 성공하게 되면 인증 및 권한 정보를 세팅하고 SecurityContext 에 저장하는 것 같습니다.

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
AccountClaims accountClaims = jwtTokenProvider.parseJwtToken(request.getHeader(HttpHeaders.AUTHORIZATION));
Account account = accountRepository.findByEmail(accountClaims.getEmail()).orElseThrow(AccountNotFoundException::new);
OAuthAccount oAuthAccount = OAuthAccount.from(account);
Authentication authentication = new UsernamePasswordAuthenticationToken(oAuthAccount, null, oAuthAccount.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}

jwtTokenProvider.parseJwtToken(request.getHeader(HttpHeaders.AUTHORIZATION))

의 실행결과가 성공이면 다음 필터로 이동하고 실패하면 FailureHandler 같은 곳으로 이동해서 실패 처리를 해 주어야 할 것 같습니다. 

jwt 인증방식은 세션을 아예 사용하지 않기 때문에 스프링 시큐리티는 인증에 성공하더라도 SecurityContext 를 세션에 저장하지 않습니다.
그렇기 때문에 위의 구현처럼 요청마다 인증과정을 거쳐야 하고 SecurityContext 에 OAuth2AuthenticationToken 를 저장하는 처리를 매번 해 주어야 합니다. 
만약 세션을 사용하는 방식이라면 한번 인증을 받으면 세션에 저장되기 때문에 그 다음부터는 인증처리가 필요하지 않게 됩니다. 
두가지 차이점에 따른 장단점이 있는 것 같습니다. 

기본적으로는 스프링 시큐리티가 인증에 성공하면 ROLE_USER 권한을 주고 있는데 별도로 ROLE_MEMBER 권한을 주고 있는것 같네요

여기까지 일단 제가 분석한 내용을 토대로 간략하게 검토해 드렸습니다. 

다음은 질의에 대한 답변입니다. 

1. 혹시 방향성 자체가 잘못 됐다면 조언을 부탁 드립니다.
방향성 자체는 문제가 없으며 OAuth2 를 통한 JWT 인증 구현은 잘못된 부분은 없는 것 같습니다. 
이 방식은 세션을 사용하지 않고 상태를 유지하지 않는 방식이기 때문에 토큰 발급에 따른 만료정책 및 보안 이슈 등을 면밀하게 살펴서 구현해야 합니다


2. addFilterBefore() 로 커스텀 필터를 등록했더니 permitAll() 이 작동하지 않습니다. 모든 요청에 관해 JWT 필터가 작동하여, 퍼블릭한 요청에 대해서도 (토큰이 없는 요청이므로) JWT 예외가 발생하고 있습니다.

사실 permitAll 은 정상적으로 작동하고 있습니다. 다만 JwtAuthorizationFilter 의 필수조건을 통과하지 못할 경우 의도치 않게 jwt 검증 자체를 타지 못하는 것이 문제이지요
그렇다면 명확하게 jwt 검증을 타지 않는 요청과 반드시 타야 하는 요청을 구분해서 설정해야 하며 전자는 토큰의 유무와 관계없이 bypass 할 수 있도록 하고 후자는 토큰이 없을 경우 예외가 발생하거나 다음 진행이 안되도록 처리하는 것이 맞습니다. 

3. 시큐리티를 통한 OAuth2 인증 과정에서 생성된 OAuth2User 객체를 전혀 사용하지 않고, 단지 유저 데이터를 생성하는 과정에서만 사용되고 있는데요. 이런 방법으로 인증을 구현해도 괜찮을지 확신이 없습니다. 인증 이후에는 사실상 SecurityContext와 인증 객체를 거의 사용하지 않는데 (OAuth2로그인과 인가 처리만 시큐리티를 통해 하고 JWT를 통해 사실상 모든 것을 하게 될 듯 합니다.), 이런 식으로 Authentication 객체나 AuthenticationPrincipal 을 적극 사용하지 않고 시큐리티를 적용해도 문제가 없을까요?

SecurityContext와 인증객체는 인증 이후의 서블릿 영역에서 필요에 따라 인증정보를 참조하고나 활용하기 위한 것이 주된 목적입니다. 

즉 JWT 인증을 받고 나서 서블릿 영역으로 접근했을 때 ThreadLocal 에 저장된 SecurityContext 객체를 전역적으로 사용하고자 함입니다. 
결론은 인증과정 이후의 사용성에 촛점을 맞추시면 됩니다. 

4. JwtAuthorizationFilter 에서 UsernamePasswordAuthenticationToken 타입으로 ContextHolder 에 저장한 Authentication 을 컨트롤러에 도달 했을때 꺼내서 사용 하고 싶습니다. 어떻게 구현 해야 할까요? (시큐리티 공부가 부족해서겠지만 방법이 잘 떠오르지 않습니다...ㅜㅜ)

일반적으로 가장 많이 작성하는 구문이 

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof OAuthAccount) {
String username = ((OAuthAccount)principal).getUsername();

} else {
String username = principal.toString();}

와 같습니다.  전역적으로 사용이 가능합니다. 

두서 없이 설명했습니다.
도움이 되셨는지 모르겠습니다. 

0

정수원님의 프로필 이미지
정수원
지식공유자

제가 질문에 대한 정확한 답변을 드리기 위해서는 프런트 쪽 화면 구성이 되어야 할 것 같습니다.

요청에 대한 흐름을 직접 실행해서 따라가 보지 않으니 제가 파악하는데 한계가 있는 것 같습니다. 

일단 질문에 대한 답변을 해 드리되 1번은 제가 전체 실행된 상태에서 테스트 하지 않으면 정확한 답변을 드리기가 어려워 2,3 번 질문에 대한 답변을 드리도록 하겠습니다.

2번 답변입니다.

credentials 필드는 자격증명이라고 해서 스프링 시큐리티에서는 비밀번호를 저장하는 용도로 사용되고 있습니다. 

그런데 비밀번호가 보안상 유출의 위험이 있어 내부적으로 필요한 처리를 한 이후 빈값으로 초기화 하고 있습니다.

그래서 내부적인 처리를 위해 꼭 필요한 경우를 제외하고 사용자에게 노출되는 영역에서는 credentials 필드에 비밀번호를 저장하지 않아야겠죠

3번 답변입니다.

보통 BasicAuthenticationFilter 는 Rest API 통신시 참조하는 모델입니다. 

비록 보안에 취약한 구조로 되어 있긴 하지만 토큰을 사용하는 방식이기 때문에 rest 보안형태로 참고하기도 합니다.

그래서 JwtAuthorizationFilter 도 BasicAuthenticationFilter 와 유사한 방식으로 구현해도 괜찮을 것 같습니다. 

그렇다고 BasicAuthenticationFilter 을 상속할 필요는 없습니다. 

OncePerRequestFilter 을 상속해서 구현하셔도 됩니다. 

각 필터의 역할과 특징만 잘 숙지한다면 굳이 BasicAuthenticationFilter 에 종속될 필요는 없을 것 같습니다. 

여기까지 해서 답변을 드렸는데 참고가 좀 되었는지 모르겠습니다.

Backend 님께서 구현하신 전체 소스를 보니 전반적으로 구성을 잘 해 놓으신 것 같습니다.

지금처럼 계속 물음표를 느낌표로 만들어 가시다 보면 좋은 결과가 반드시 있게 되실거라 믿습니다.

화이팅 하십시오^^!!

0

Backend.dev님의 프로필 이미지
Backend.dev
질문자

정말 감사합니다.

아직 테스트는 더 해봐야겠지만, 알려주시고 조언 해주신 것들을 바탕으로 드디어 구현에 성공 했습니다.

구현에는 성공 하였지만 시행 착오를 겪었던 과정에서 이해하지 못한 것들이 있어 몇 가지 질문을 드리고 싶습니다. (아마도 마지막 질문이 될거 같습니다.)



1. 앞선 질문에서 2번 질문에 대한 답변에서 AbstractAuthenticationToken을 상속하여 커스텀 토큰을 만드는 방법을 알려 주셨습니다.

그래서 다음과 같이 AuthenticationToken을 추가 했는데요.

public class CustomJwtAuthenticationToken extends AbstractAuthenticationToken {

private final OAuthAccount oAuthAccount;
private final String jwtToken;

@Builder
private CustomJwtAuthenticationToken(OAuthAccount oAuthAccount, String jwtToken) {
super(oAuthAccount.getAuthorities());
this.oAuthAccount = oAuthAccount;
this.jwtToken = jwtToken;
}

@Override
public Object getCredentials() {
return jwtToken;
}

@Override
public Object getPrincipal() {
return oAuthAccount;
}
}

이를 커스텀 필터에서 UsernamePasswordAuthenticationToken 대신 생성 하고 SecurityContextHolder에 저장 하였는데, 요청 테스트를 해보니 StackoverflowError Exception이 발생 하였습니다. 

이전 3번 질문에 대한 답변에 AuthenticationManager 는 부모 AuthenticationManager  객체를 가질 수 있고 재귀해서 적절한 AuthenticationProvider 를 찾는다고 되어 있습니다.  라는 내용이 있었습니다.

SecurityConfig에 임의로 생성한 AuthenticationManager를 넣어 주었지만, 인증 과정에서 AuthenticationProvider를 찾지 못하여 재귀가 발생하여 생긴 예외라 추측을 하고 있는데, 이 것이 맞을까요?

Custom 토큰이 아닌 기존에 있는 UsernamePasswordAuthenticationToken을 사용하면 해당 예외가 발생하지 않습니다.




2. JWT를 인증하는 커스텀 필터에서 

OAuthAccount oAuthAccount = OAuthAccount.from(account);
Authentication authentication = new UsernamePasswordAuthenticationToken(oAuthAccount, jwtToken, oAuthAccount.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);

위와 같이 UsernamePasswordAuthenticationToken에 credentials에 jwtToken을 넣어주고 있지만, 이전 처럼 이 부분을 비워두어도 문제가 생기지 않는데요. (값을 바꿔 보아도 정상 작동 하는 것을 확인할 수 있었습니다.) 이와 같이 토큰을 직접 생성하는 과정에서 credentials 필드가 어떤 의미를 가지는건지 알고 싶습니다.




3. 기존에는 커스텀 인증 필터가 BasicAuthenticationFilter를 상속하도록 구현을 했었는데, 해당 필터 안에서는 authenticationManager의 역할이 전혀 없다고 생각되어 OncePerRequestFilter를 상속 하도록 수정 해놓은 상태 입니다. 이 역시 코드가 문제 없이 작동을 하는데, 엄연히 인증 처리를 대신하는 필터이기 때문에 BasicAuthenticationFilter를 상속하도록 구현 하는게 맞는건지 판단에 어려운 점이 있습니다.

BasicAuthenticationFilter가 OncePerRequestFilter를 상속하는 필터라는 것은 알고 있는데, 어떤 필터를 상속하도록 하는게 더 옳은 쪽일까요?



여기까지 아마도 마지막으로 드리는 질문이 될거 같습니다. 시큐리티가 공부할 자료도 충분하지 않고 공식 문서도 친절한편이 아니라 어려움이 많았는데요. 제공해주신 강의를 통해 적용을 시도해볼 수 있었고, 인증 / 인가의 개념을 다지는데에도 정말 많은 도움을 받았습니다. 다시 한번 감사를 드립니다.

여전히 어렵고 모르는게 많지만 이번 구현 과정에서 좀 더 학습이 된 느낌인데요. 프로젝트를 마무리 하면 남은 실전 강의도 끝까지 완강하고 수강평도 남기겠습니다 :)

도움 주셔서 정말 감사합니다.

0

정수원님의 프로필 이미지
정수원
지식공유자

1. 처음 질문에서 설명 드린것과 같이 OAuth2로 사용자를 인증한 뒤 이후에는 JWT 토큰을 통해 인증 / 인가 처리를 하는 흐름 인데요.
SecurityContext에 Authentication을 넣어주는 과정에서 OAuth2LoginAuthenticationToken이 아닌 UsernamePasswordAuthenticationToken을 사용해도 문제가 없을까요?

딱히 구현상으로는 문제될 것은 없습니다. 활용도에 있어서 단순히 사용자 정보만 참조한다고 한다면요

OAuth2LoginAuthenticationToken 도 Authentication 을 구현했기 때문에 타입변환에도 문제가 없습니다. 다만 인증 이후에 OAuth2LoginAuthenticationToken 이 필요한 경우가 생긴다면 그에 따라 판단하시면 될 것 같습니다. 

그런데 혹 OAuth2 와 Form 인증을 병행해서 사용하는 경우가 발생한다면 별도로 분리하는 것이 맞습니다.

그리고 주의 하실 점은 인증 후 최종 결과를 저장하는 경우에는 상관없지만 인증을 시도하는 경우에는 인증 클래스 타입에 따라 AuthenticationProvider 타입이 결정되기 때문에 OAuth2LoginAuthenticationToken 를 UsernamePasswordAuthenticationToken 로 대체하면 안됩니다.

2. 만약 UsernamePasswordAuthenticationToken을 사용해도 괜찮다면, UserDetails 타입으로 넘기지 않고 OAuth2User 타입의 객체를 넘겨도 상관이 없을까요? (UserDetails로 생성하는 것이 더 간단할 듯 하지만, Password를 사용하지 않는다는 것이 걸리는 점 같습니다.)

이것도 크게 상관은 없습니다. 

스프링 시큐리티가 인증방식에 따른 인증클래스를 다르게 구성한 건 맞지만 principal 이 Object 이기 때문에 OAuth2User 를 저장해도 괜찮습니다. 

단순하게 처리하는 것이 목적이라면 AbstractAuthenticationToken 이나 UsernamePasswordAuthenticationToken 을 상속한 CustomOAuth2LoginAuthenticationToken 을 생성해서 사용하는 것도 하나의 방법입니다. 

이것도 역시 인증을 시도하는 경우에는 제외해야 합니다. 



3. 답변입니다.

위 화면은 

@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}

이 실행되면 생성되는 AuthenticatoinManager 의 정보입니다. 

Proxy121  로 생성되어 있습니다. 

그리고 아래 화면을 보시면

OAuth2LoginAuthenticationFilter 클래스의 초기화 과정입니다.
OAuth2 인증 시에도 AuthenticationManager 를 사용해서 인증처리를 합니다. 

1번 동그라미 객체가 기본 AuthenticationManager  구현체인 ProviderManager 입니다.
2번 동그라미 객체가 위 화면의 SecurityConfig 에서 만든 AuthenticationManager 의 구현체인 Proxy121 객체이고 1번 객체의 parent 속성에 저장되어 있습니다.

AuthenticationManager  챕터 강의를 보시면 AuthenticationManager 는 부모 AuthenticationManager  객체를 가질 수 있고 재귀해서 적절한 AuthenticationProvider 를 찾는다고 되어 있습니다. 

즉 OAuth2LoginAuthenticationFilter 가 기본 AuthenticationManager 즉 1번 객체를 가지고 검증을 시도하는데 적절한 AuthenticationProvider 가 없을 경우 2번 AuthenticationManager 인 Proxy121 객체를 통해 다시 AuthenticationProvider 를 찾게 됩니다. 
그래서 SecurityConfig 에서 생성한다고 문제가 되지 않습니다. 

4. 답변입니다.

OAuth2LoginAuthenticationToken 은 OAuth2LoginAuthenticationFilter 의 인증과정에서 사용하는 클래스입니다. 인증 이후에는 SecurityContext 에 저장해서 꺼내어 사용가능하겠죠

다만 OAuth2LoginAuthenticationToken 자체를 별도의 필터를 만들어서 사용하는 부분은 그렇게 간단하지 않을 것 같습니다.

결국은 OAuth2LoginAuthenticationFilter 가 하는 역할을 정확하게 알고 참조해서 사용해야 되는데 굳이 그럴만한 상황이 발생할지는 잘 모르겠습니다.

사용하는 것 자체가 문제라기보다는 정확한 사용처와 목적 그리고 자세한 개념이 바탕이 되어 사용하는 것이 더 중요하다고 생각합니다.

감사합니다.

0

Backend.dev님의 프로필 이미지
Backend.dev
질문자

선생님, 먼저 자세한 답변 해주셔서 정말 감사합니다 :)



알려주신 것을 바탕으로 코드를 분석하고 다시 수정하려 시도를 하고 있는데요. 잘 해결 되지 않는 부분들이 있어 부득이하게 추가 질문을 드리게 되었습니다.



먼저 시큐리티가 내부적으로 OAuth2LoginAuthenticationProvider를 통해 OAuth2LoginAuthenticationToken을 생성하여, 이를 SecurityContextHolder에 저장 하는 과정을 디버거를 통해 확인 하였습니다. 

문제는 말씀 하신 것처럼 JWT 인증방식은 아래와 같은 설정으로

http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);

세션을 사용하지 않기 때문에, 인가 프로세스 이전에 SecurityContext에 Authentication을 별도로 저장해주어야 한다는 점입니다.

그래서 제가 처음에 시도 했던 방법은 UsernamePasswordAuthenticationToken을 수동으로 생성해서 SecurityContext에 넣어주는 방법이었습니다.

사실 이 방법을 택했던 이유는, OAuth2LoginAuthenticationToken에 대해 숙지하고 있지 못하기 때문이었던거 같습니다.



기존 인증 / 인가 처리 흐름에 문제는 없다고 말씀을 해주셨고, 제가 최종적으로 구현하고 싶은 것은 다음과 같습니다.


1. 다음과 같이 인증이 끝난 사용자가 BasicAuthenticationFilter 를 거쳐갈 때 (인가 처리 필터에 도달하기 이전에)

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
AccountClaims accountClaims = jwtTokenProvider.parseJwtToken(request.getHeader(HttpHeaders.AUTHORIZATION));
Account account = accountRepository.findByEmail(accountClaims.getEmail()).orElseThrow(AccountNotFoundException::new);
OAuthAccount oAuthAccount = OAuthAccount.from(account);
Authentication authentication = new UsernamePasswordAuthenticationToken(oAuthAccount, null, oAuthAccount.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}

JWT를 검증하고, 검증된 사용자라면

UserDetails 타입 혹은 OAuth2User 를 implement 한 객체(Authentication)를 생성하여 SecurityContextHolder에 저장한다.

2. 사용자에 대한 인가 처리를 한다.
3. 인가 처리가 끝난 사용자가 컨트롤러에 도달하면, SecurityContextHolder에서 꺼내어 이후 처리를 진행한다.

입니다.

여기서 궁금한 점이 있습니다.



1. 처음 질문에서 설명 드린것과 같이 OAuth2로 사용자를 인증한 뒤 이후에는 JWT 토큰을 통해 인증 / 인가 처리를 하는 흐름 인데요.
SecurityContext에 Authentication을 넣어주는 과정에서 OAuth2LoginAuthenticationToken이 아닌 UsernamePasswordAuthenticationToken을 사용해도 문제가 없을까요?

2. 만약 UsernamePasswordAuthenticationToken을 사용해도 괜찮다면, UserDetails 타입으로 넘기지 않고 OAuth2User 타입의 객체를 넘겨도 상관이 없을까요? (UserDetails로 생성하는 것이 더 간단할 듯 하지만, Password를 사용하지 않는다는 것이 걸리는 점 같습니다.)



3. UsernamePasswordAuthenticationToken을 사용해도 괜찮을 경우, 여기서 궁금한 것이 AuthenticationManager의 역할 입니다. 기존 코드에 아래와 같이 시큐리티 설정이 되어 있습니다.

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {


// ...



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

// ...


http.addFilterBefore(JwtAuthorizationFilter.of(authenticationManager(), accountRepository, jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);


// ...

}

@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}

BasicAuthenticationFilter를 extends 하려면 반드시 authenticationManager 를 생성자에 넣어주어야 하기 때문에

@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}

위의 코드를 추가 했던 것인데요.

선생님 강의를 통해 AuthenticationManager의 역할이 인증처리를 위임하는 인터페이스라 배웠었습니다.
문제는 소셜 로그인이기 때문에 사용자가 OAuth를 통해 인증을 하는 과정에서 AuthenticationManager를 별도로 사용하지 않았는데, 이렇게 임의로 넣어 필터를 생성한 뒤 다음과 같이 

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
AccountClaims accountClaims = jwtTokenProvider.parseJwtToken(request.getHeader(HttpHeaders.AUTHORIZATION));
Account account = accountRepository.findByEmail(accountClaims.getEmail()).orElseThrow(AccountNotFoundException::new);
OAuthAccount oAuthAccount = OAuthAccount.from(account);
Authentication authentication = new UsernamePasswordAuthenticationToken(oAuthAccount, null, oAuthAccount.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}

구현해도 문제가 없을까요?



4. 만약 반드시 OAuth2LoginAuthenticationToken을 사용해야 한다면 

public OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,
OAuth2AuthorizationExchange authorizationExchange,
OAuth2User principal,
Collection<? extends GrantedAuthority> authorities,
OAuth2AccessToken accessToken) {
this(clientRegistration, authorizationExchange, principal, authorities, accessToken, null);
}

이 생성자를 통해 생성을 하는 듯 보이는데요. (OAuth2AccessToken이 있는 것으로 봐서는 OAuth Authorization 서버와 통신을 하는 과정에서 쓰이는 것 같기도 한데...) 이 것들을 필터에서 생성하고 활용할 수 있는 방법이 있는지 궁금합니다. 



프로젝트를 시작하면서부터 시큐리티를 통한 OAuth2 + JWT 사용자 인증 / 인가에 대해 정말 많이 찾아봤습니다. 그런데 자료도 많이 없고 여전히  답을 찾지 못하고 있네요ㅠㅠ

시큐리티의 인증 / 인가 처리가 매우 견고하고 강력하다고 느끼고 있기 때문에 JWT를 적용하는 과정에서 꼭 사용 해보고 싶은데... 쉽지 않네요. 조언을 부탁 드립니다.



감사합니다.

0

정수원님의 프로필 이미지
정수원
지식공유자

사실 OAuth2, JWT 관련 기술을 스프링 시큐리티에서 적극적으로 지원하지 않는 것으로  알고 있습니다.

물론 현재 제공되고 있는 라이브러리를 가지고도 인증 서버 기술을 구현할 수 있습니다.

강의제작도 고려하고 있지만 스프링 시큐리티가 OAuth2 기술에 대한 지원 및 방향을 어떤 식으로 끌고 나가느냐에 따라 지켜 보고 있는 상태입니다.

일단 메일로 주시면 제가 검토 해 드릴 수는 있으나 만족할 만한 답변을 드리지 못할 수 있음을 양해 부탁드립니다.

onjsdnjs@gmail.com 입니다

감사합니다.

Backend.dev님의 프로필 이미지
Backend.dev

작성한 질문수

질문하기