React 로그인 Modal 만들기 6 - Login 구현

React 로그인 Modal 만들기 6 - Login 구현

Json 인증 Filter 구현하기

UsernamePasswordAuthenticationFilter 의 경우 Form Login을 위해 구현된 Filter라 Request Body로 요청이 들어오는 Login 요청을 적용하기는 어렵기에 JSON 을 이용해 Login을 진행할 수 있도록 JsonAuthenticationFilter 를 새로 만들어줄 필요가 있다.

UsernamePasswordAuthenticationFilter 와 인증을 진행하는 과정은 비슷하므로 AbstractAuthenticationProcessingFilter 를 상속해 새로운 Filter를 생성해준다.

JsonAuthenticationFilter.java

public class JsonAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

private static final String LOGIN_PROCESSING_URL = "/api/login";

@Autowired
private ObjectMapper objectMapper;

public JsonAuthenticationFilter() {
super(new AntPathRequestMatcher(LOGIN_PROCESSING_URL));
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if(!isApplicationJSON(request)){
throw new IllegalStateException("Content Type is not Application/json");
}
LoginDto loginDto = objectMapper.readValue(request.getReader(), LoginDto.class);

if(ObjectUtils.isEmpty(loginDto.getUsername()) || ObjectUtils.isEmpty(loginDto.getPassword())){
throw new IllegalArgumentException("Username or Password is empty");
}

JsonAuthenticationToken jsonAuthenticationToken = new JsonAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());
getAuthenticationManager().authenticate(jsonAuthenticationToken);

return null;
}

private boolean isApplicationJSON(HttpServletRequest httpServletRequest){
if(httpServletRequest.getHeader("Content-type").equals(MediaType.APPLICATION_JSON_VALUE)){
return true;
}
return false;
}
}

새로운 Authentication Token 만들기

JsonAuthenticationFilter 에서 사용하기 위한 새로운 Authentication 생성

JsonAuthenticationToken.java

public class JsonAuthenticationToken extends AbstractAuthenticationToken {

private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

private final Object principal;

private Object credentials;

public JsonAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}


public JsonAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}

@Override
public Object getCredentials() {
return this.credentials;
}

@Override
public Object getPrincipal() {
return this.principal;
}

@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
Assert.isTrue(!isAuthenticated,
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
super.setAuthenticated(false);
}

@Override
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}

JsonAuthenticationToken 객체 인증을 위한 AuthenticationProvider 생성

JsonAuthenticationToken 객체를 이용해 인증을 진행할 수 있도록 새로운 AuthenticationProvider 를 생성한다.

  • supports 메소드에서 전달받은 Authentication 객체 타입이 JsonAuthenticationToken 인지 확인
  • authenticate 메소드에서 전달받은 Authentication 객체를 이용해 인증을 진행 한 후 새로운 JsonAuthenticationToken 객체 생성

JsonAuthenticationProvider.java

@RequiredArgsConstructor
public class JsonAuthenticationProvider implements AuthenticationProvider {

private final PasswordEncoder passwordEncoder;
private final UserDetailsService userDetailsService;


@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();


UserDetails loadUserByUsername = userDetailsService.loadUserByUsername(username);

if(!passwordEncoder.matches(password, loadUserByUsername.getPassword())){
throw new BadCredentialsException("Invalid password");
}

return new JsonAuthenticationToken(loadUserByUsername, authentication.getCredentials(), loadUserByUsername.getAuthorities());
}

@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(JsonAuthenticationToken.class);
}
}

인증 성공 후 처리릉 위한 SuccessHandler 생성

AuthenticationSuccessHandler 를 구현해 인증에 성꽁하면 HttpStatus 200(OK) 를 반환하도록 한다.

JsonAuthenticationSuccessHandler.java

@RequiredArgsConstructor
public class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
try {
response.setStatus(HttpStatus.OK.value());
} catch (Exception e) {
throw new ServletException("inavalid username/password");
}
}
}

Security Config 추가 하기

로그인으로 Json 데이터가 왔을 때 인증을 진행하기 위해 새로 만든 JsonAuthenticationFilter , JsonAuthenticationProvider , JsonAuthenticationSuccessHandler 를 Bean으로 등록해 인증에서 사용할 수 있도록 한다.

@Configuration
@EnableWebSecurity
@Order(0)
@RequiredArgsConstructor
public class JsonSecurityConfig extends WebSecurityConfigurerAdapter {

private final UserInfoRepository userInfoRepository;
private final ObjectMapper objectMapper;
private final JwtUtils jwtUtils;


@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider())
.userDetailsService(userDetailsService());
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();

http
.addFilterBefore(jsonAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Bean
public UserDetailsService userDetailsService() {
return new UserInfoService(userInfoRepository, passwordEncoder());
}

@Bean
public AuthenticationProvider authenticationProvider() {
return new JsonAuthenticationProvider(passwordEncoder(), userDetailsService());
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public JsonAuthenticationSuccessHandler jsonAuthenticationSuccessHandler(){
return new JsonAuthenticationSuccessHandler(objectMapper, jwtUtils);
}

@Bean
public JsonAuthenticationFilter jsonAuthenticationFilter() throws Exception {
JsonAuthenticationFilter jsonAuthenticationFilter = new JsonAuthenticationFilter();
jsonAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
jsonAuthenticationFilter.setAuthenticationSuccessHandler(jsonAuthenticationSuccessHandler());

return jsonAuthenticationFilter;
}
}
Share