꾸물꾸물 졔의 개발공부
[Springboot] JWT(3)_filter 적용 및 토큰 검사/인증 부여 본문
JWT(1)포스팅에서 사용자 로그인시, access 토큰과 refresh 토큰을 발급해 주었고,
JWT(2)포스팅에서 refresh 토큰의 유효기간 만큼 redis 에 저장했다.
나는, 회원정보 조회나 수정 또는 회원탈퇴와 같이 사용자정보 확인의 보안 절차가 필요한 과정에서는 토큰을 검사하여
유효한 사용자만 가능하도록 설정하고 싶었다.
▶ 내가 설계한 프로젝트의 구조는
- 클라이언트의 요청을 서비스 서버에서 받고,
- 회원관리와 같이 사용자 개인정보 관련 로직은 인증서버에서 처리하도록 하였다.
- 나는 인증서버에 토큰 검사를 위한 필터를 적용하려고 한다.
JwtTokeUtil.java - 토큰을 생성/검증/에러 핸들링 하는 파일
▶ getVerifier()
//토큰 검증기
public static JWTVerifier getVerifier() {
return JWT.require(Algorithm.HMAC512(secretKey.getBytes()))
.withIssuer(ISSUER)
.build();
}
토큰 검증
→ jwt 검증 메서드 ( 내부적으로 decode 도 같이 이루어짐 )
▶ handleError()
public static void handleError(String token) {
JWTVerifier verifier = JWT
.require(Algorithm.HMAC512(secretKey.getBytes()))
.withIssuer(ISSUER)
.build();
try {
verifier.verify(token.replace(TOKEN_PREFIX, ""));
} catch (AlgorithmMismatchException ex) {
throw ex;
} catch (InvalidClaimException ex) {
throw ex;
} catch (SignatureGenerationException ex) {
throw ex;
} catch (SignatureVerificationException ex) {
throw ex;
} catch (ExpiredJwtException ex) {
throw ex;
} catch (JWTCreationException ex) {
throw ex;
} catch (JWTDecodeException ex) {
throw ex;
} catch (JWTVerificationException ex) {
throw ex;
} catch (Exception ex) {
throw ex;
}
}
토큰 검증과정에서 발생하는 에러 및 예외 처리
SssUserDetailsService.java - 필터를 거쳐 인증된 사용자는 DB에서 사용자 정보를 찾아와 , 객체에 저장
@Service
@RequiredArgsConstructor
public class SssUserDetailService implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
User user= userRepository.findUserById(id).orElseThrow(
()-> new UsernameNotFoundException("해당 id의 회원이 존재하지 않습니다"));
SssUserDetails userDetails = new SssUserDetails(user);
return userDetails;
}
}
SssUserDetails.java - 해당 객체
public class SssUserDetails implements UserDetails {
@Autowired
User user;
public SssUserDetails(User user) {
super();
this.user=user;
}
public User getUser() {
return this.user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
String roleName= user.getRole();
authorities.add(()->roleName);
return authorities;
}
@Override
public String getPassword() {
return this.user.getPassword();
}
@Override
public String getUsername() {
return this.user.getId();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
JwtAuthorizationFilter.java - 토큰의 유효성을 검사하고, 사용자에게 권한을 부여
public class JwtAuthorizationFilter extends BasicAuthenticationFilter{
@Autowired
UserRepository userRepo;
public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserRepository userRepo) {
super(authenticationManager);
this.userRepo = userRepo;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException{
String header =request.getHeader(JwtTokenUtil.HEADER_STRING);
if(header == null || !header.startsWith(JwtTokenUtil.TOKEN_PREFIX)) {
chain.doFilter(request, response);
return;
}
try {
//앞부분을 떼어내고 토큰값 추출, 인증정보얻어오기
Authentication authentication = getAuthentication(request);
// jwt 토큰으로 부터 획득한 인증 정보(authentication) 설정.
SecurityContextHolder.getContext().setAuthentication(authentication);
}catch(TokenExpiredException e){
log.debug("filter-유효기간만료");
request.setAttribute("exception", ErrorCode.EXPIRED_TOKEN.getErrorCode());
}catch(Exception ex) {
log.debug("filter-유효하지않은토큰");
request.setAttribute("exception", ErrorCode.INVALID_TOKEN.getErrorCode());
}
chain.doFilter(request, response);
}
@Transactional(readOnly = true)
public Authentication getAuthentication(HttpServletRequest request) throws Exception{
String token = request.getHeader(JwtTokenUtil.HEADER_STRING);
//토큰 검증기 받아오기
JWTVerifier verifier = JwtTokenUtil.getVerifier();
//토큰에 이상이 있는지 ? 있으면 예외 ------> 유효기간 만료시 , 토큰 재발급 하기 위한 리프레시 토큰 받아야함
JwtTokenUtil.handleError(token);
//실제 검증 + 복호화(내부적으로 복호화도 해줌)
DecodedJWT decodedJWT = verifier.verify(token.replace(JwtTokenUtil.TOKEN_PREFIX, ""));
String id = decodedJWT.getSubject();
if(id != null) {
// 계정 정보 아이디로, 실제 디비에 해당 유저가 있는지 조회
User user= userRepo.findUserById(id).orElseThrow(
()->new Exception("해당 id 정보를 갖는 회원이 없습니다."));
// 디비에 저장되있는 경우, 인증 정보 생성
SssUserDetails userDetails = new SssUserDetails(user);
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
return authentication;
}
return null ;
}
}
▶ doFilterInternal
: 요청헤더에 값이 담겨있는지 확인한 후, 값이 있다면 그것이 토큰인지 확인한다.
→ 토큰의 형태라면, bearer 값 포함
- 토큰이 없다면, 다음 필터체인으로 넘긴다. ( 토큰이 필요없는 요청일 수도 있음 )
- 토큰이 있다면, 해당 토큰의 유효성을 검사하고, 유효한 토큰이라면 인증정보를 얻어와서 SecurityContextHolder 에 해당 인증정보를 저장한다.
- 토큰이 있지만, 토큰이 유효기간 지났거나, 유효하지 않은 값이라면 예외 발생시키기 ( 다음포스팅에서 계속 .. )
▶ getAuthentication
: 토큰 값이 있다면 , 해당 토큰 값이 올바른 값인지 또는 유효하지 않은 값인지 검증
→ 유효한 토큰이라면, DB 에서 해당 유저 정보를 불러와, UserDetails 객체에 저장하고, ( SssUserDetails 이 상속 )
UsernamePasswordAuthenticationToken 으로 인증정보 설정
→ 해당 인증정보로 , 사용자가 유효한 사용자인지 검증
SecurityConfig.java - 필터 등록
@Override
protected void configure(HttpSecurity http) throws Exception{
http.httpBasic().disable()
.formLogin().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.addFilter(corsFilter())
.addFilter(new JwtAuthorizationFilter(authenticationManager(), userRepo)).authorizeRequests()
.antMatchers("/authentication/**").authenticated()
.anyRequest().permitAll();
}
SecurityConfig.java 에서 필터 등록
- .addFilter() : JwtAuthorizationFilter 등록,
- .authorizaeRequests() : 인증절차에 대한 설정 시작 표기
- .antMatchers(url).authenticated() : 해당 url 에 대한 접근은 인증 받은 사용자만 접근 가능
- .anyRequests().permitAll() : 그 외의 url 접근은 누구나 가능
'SPRING' 카테고리의 다른 글
[Springboot] JWT(5)_ WebClient 통신 (서버 투 서버)에서 JWT 필터 예외 ( 유효 기간 만료 / 올바르지 않은 값 ) 처리하기 (0) | 2022.08.23 |
---|---|
[Springboot] JWT(4)_ JWT 필터를 통해 토큰 만료시간 / 토큰 유효성 예외 발생시키기 (0) | 2022.08.23 |
[Springboot] JWT(2)_refresh 토큰 redis 저장 (0) | 2022.08.16 |
[Springboot] JWT(1)_로그인, 토큰 발급하기 ( access + refresh ) (0) | 2022.08.16 |
[Springboot] 회원가입 이메일 인증하기 (네이버) (0) | 2022.08.06 |