작성
·
469
·
수정됨
0
실습중 manager로 로그인해도 계속 401에러가발생하여
/api/messages에 대해 permitAll권한을주어 테스트해보았는데 위에 스크린샷처럼 Anonymous토큰상태여서 발생하는듯한데 프로바이더에서 토큰처리는 완료한 상태입니다.
로그인도 정상적으로 되고있구요. 뭔가 해야 할 작업이 빠진걸까요?
스프링은 3.03버젼이고 스프링시큐리티도 6버전대입니다
package io.security.corespringsecurity.security.config;
import io.security.corespringsecurity.security.common.AjaxAccessDeniedHandler;
import io.security.corespringsecurity.security.common.AjaxLoginAuthenticationEntryPoint;
import io.security.corespringsecurity.security.filter.AjaxLoginProcessingFilter;
import io.security.corespringsecurity.security.habdler.AjaxAuthenticationFailureHandler;
import io.security.corespringsecurity.security.habdler.AjaxAuthenticationSuccessHandler;
import io.security.corespringsecurity.security.provider.AjaxAuthenticationProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@Order(0)
@RequiredArgsConstructor
public class AjaxSecurityConfig {
private final AuthenticationConfiguration authenticationConfiguration;
@Bean
public AjaxAuthenticationProvider ajaxAuthenticationProvider(){
return new AjaxAuthenticationProvider();
}
@Bean
public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler(){
return new AjaxAuthenticationSuccessHandler();
}
@Bean
public AuthenticationFailureHandler ajaxAuthenticationFailureHandler(){
return new AjaxAuthenticationFailureHandler();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.authorizeHttpRequests(requests -> requests
.requestMatchers("/api/**").permitAll()
.requestMatchers(HttpMethod.GET,"/api/messages").hasRole("MANAGER")
.anyRequest().authenticated());
http
.exceptionHandling()
.authenticationEntryPoint(new AjaxLoginAuthenticationEntryPoint())
.and()
.addFilterBefore(ajaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
http.csrf().disable();
return http.getOrBuild();
}
@Bean
public AccessDeniedHandler ajaxAccessDeniedHandler(){
return new AjaxAccessDeniedHandler();
}
@Bean
public AuthenticationManager authenticationManager() throws Exception{
ProviderManager providerManager = (ProviderManager)authenticationConfiguration.getAuthenticationManager();
providerManager.getProviders().add(ajaxAuthenticationProvider());
return providerManager;
}
@Bean
public AjaxLoginProcessingFilter ajaxLoginProcessingFilter() throws Exception{
AjaxLoginProcessingFilter ajaxLoginProcessingFilter = new AjaxLoginProcessingFilter();
ajaxLoginProcessingFilter.setAuthenticationManager(authenticationManager());
ajaxLoginProcessingFilter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler());
ajaxLoginProcessingFilter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler());
return ajaxLoginProcessingFilter;
}
}
package io.security.corespringsecurity.security.provider;
import io.security.corespringsecurity.security.service.AccountContext;
import io.security.corespringsecurity.security.token.AjaxAuthenticationToken;
import io.security.corespringsecurity.util.PBKDF2Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
@Component
public class AjaxAuthenticationProvider implements AuthenticationProvider{
@Autowired
private UserDetailsService userDetailsService;
@Transactional
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String)authentication.getCredentials();
AccountContext accountContext = (AccountContext)userDetailsService.loadUserByUsername(username);
try {
if(!PBKDF2Util.validatePassword(password, accountContext.getAccount().getPassword())){
throw new BadCredentialsException("BadCredentialsException");
}
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException(e);
}
return new AjaxAuthenticationToken(accountContext.getAccount(), null, accountContext.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(AjaxAuthenticationToken.class);
}
}
package io.security.corespringsecurity.security.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.security.corespringsecurity.domain.AccountDto;
import io.security.corespringsecurity.security.token.AjaxAuthenticationToken;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.thymeleaf.util.StringUtils;
import java.io.IOException;
public class AjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter {
private ObjectMapper objectMapper = new ObjectMapper();
public AjaxLoginProcessingFilter() {
super(new AntPathRequestMatcher("/api/login"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if(!jsAjax(request)){
throw new IllegalStateException("Authentication is not supported");
}
AccountDto accountDto = objectMapper.readValue(request.getReader(), AccountDto.class);
if(StringUtils.isEmpty(accountDto.getUsername()) || StringUtils.isEmpty(accountDto.getPassword())){
throw new IllegalArgumentException("Username or Password is empty");
}
AjaxAuthenticationToken authenticationToken = new AjaxAuthenticationToken(accountDto.getUsername(), accountDto.getPassword());
return getAuthenticationManager().authenticate(authenticationToken);
}
private boolean jsAjax(HttpServletRequest request) {
if("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))){
return true;
}
return false;
}
}
답변 2
0
답변이 늦어 죄송합니다.
먼저 시큐리티 버전을 강의 버전으로 해 보시고 결과가 정상으로 나온다면 버전차이로 인한 호환성 부분이 원인일 것 같습니다.
그렇지 않고 강의버전으로 해도 오류가 난다면 전체 코드를 공유해 주시기 바랍니다.
그리고 최신 버전으로 인한 오류에 대한 정확한 원인과 답변을 하기는 현재로선 어려운 점 양해 부탁드립니다.
0
안녕하세요 얼티밋 님, 인프런 AI 인턴이에요.
코드를 살펴보니, AjaxSecurityConfig
클래스에서 /api/messages
에 대해 permitAll
권한을 주고 있기 때문에 인증 없이 접근할 수 있는 상태가 됩니다. 이로 인해 Authentication
이 AnonymousToken
으로 설정되고 401 에러가 발생하는 것 같습니다.
해결하기 위해서는 이 부분을 수정하셔야 합니다. /api/messages
에 대해서도 인증이 필요하도록 설정해야 합니다. 예를 들어, hasRole("MANAGER")
를 사용하여 해당 URL에 대해 MANAGER
권한을 가진 사용자만 접근할 수 있도록 설정할 수 있습니다. 다음과 같이 코드를 수정해보세요:
http
.authorizeHttpRequests(requests -> requests
.antMatchers("/api/messages").hasRole("MANAGER")
...
);
또한, AjaxLoginProcessingFilter
클래스의 jsAjax
메소드를 다음과 같이 수정하여 XMLHttpRequest
헤더를 사용하는 경우에만 true
를 반환하도록 수정할 수 있습니다:
private boolean jsAjax(HttpServletRequest request) {
String ajaxHeader = request.getHeader("X-Requested-With");
return "XMLHttpRequest".equals(ajaxHeader);
}
이렇게 수정한 후 변경된 코드를 다시 테스트해 보세요. 도움이 되었길 바랍니다.