JWT 강의를 보며 기본 개념을 익히고 우리팀에 적용해보기로 했다.
public class FormLoginFilter extends UsernamePasswordAuthenticationFilter {
final private ObjectMapper objectMapper;
public FormLoginFilter(final AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
UsernamePasswordAuthenticationToken authRequest;
try {
JsonNode requestBody = objectMapper.readTree(request.getInputStream());
String username = requestBody.get("username").asText();
String password = requestBody.get("password").asText();
authRequest = new UsernamePasswordAuthenticationToken(username, password);
} catch (Exception e) {
throw new IllegalArgumentException("username, password 입력이 필요합니다. (JSON)");
}
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
// FormLoginFilter 에서 생성된 토큰으로부터 아이디와 비밀번호 조회
String username = token.getName();
String password = (String) token.getCredentials();
// UserDetailsService 를 통해 DB에서 username 으로 사용자 조회
UserDetailsImpl userDetails = (UserDetailsImpl) userDetailsService.loadUserByUsername(username);
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException(userDetails.getUsername() + "Invalid password");
}
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
}
public class FormLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
public static final String AUTH_HEADER = "Authorization";
public static final String TOKEN_TYPE = "BEARER";
@Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response,
final Authentication authentication) {
final UserDetailsImpl userDetails = ((UserDetailsImpl) authentication.getPrincipal());
// Token 생성
final String token = JwtTokenUtils.generateJwtToken(userDetails);
response.addHeader(AUTH_HEADER, TOKEN_TYPE + " " + token);
}
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String token = (String) authentication.getPrincipal();
String username = jwtDecoder.decodeUsername(token);
// TODO: API 사용시마다 매번 User DB 조회 필요
// -> 해결을 위해서는 UserDetailsImpl 에 User 객체를 저장하지 않도록 수정
// ex) UserDetailsImpl 에 userId, username, role 만 저장
// -> JWT 에 userId, username, role 정보를 암호화/복호화하여 사용
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Can't find " + username));;
UserDetailsImpl userDetails = new UserDetailsImpl(user);
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
}