Spring Security Spring安全插件应该响应401而不是403

7kjnsjlb  于 2023-06-23  发布在  Spring
关注(0)|答案(3)|浏览(194)

当Web会话过期时,Spring Security会返回403 HTTP状态。理想情况下,它会以401响应。未授权和禁止是不同的。如果有一个有效的会话,对安全资源的请求应该只返回403,但用户只是没有对所述资源的权限。如果资源是安全的,并且没有经过身份验证的会话,那么Spring Security应该返回401。
我的应用程序需要非常具体地区分这两个错误代码。
我的问题是,如何自定义此行为?关于401和403之间的区别,请阅读本文。

bfrts1fy

bfrts1fy1#

以下是我的解决方案:

@Configuration
public class WebCtxConfig  implements BeanPostProcessor {

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof SessionManagementFilter) {
                SessionManagementFilter filter = (SessionManagementFilter) bean;
                filter.setInvalidSessionStrategy(new InvalidSessionStrategy() {

                    @Override
                    public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
                        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                    }
                });
            }
            return bean;
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    }
nkcskrwz

nkcskrwz2#

在Sping Boot 2.2.1中,我使用了从AuthenticationEntryPoint派生的类:

import java.io.IOException;
import javassist.NotFoundException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class AppAuthenticationEntryPoint implements AuthenticationEntryPoint{

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException auth) throws IOException, ServletException {
        // 401
        setResponseError(response, HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
    }

    @ExceptionHandler (value = {AccessDeniedException.class})
    public void commence(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        // 403
        setResponseError(response, HttpServletResponse.SC_FORBIDDEN, String.format("Access Denies: %s", accessDeniedException.getMessage()));
    }

    @ExceptionHandler (value = {NotFoundException.class})
    public void commence(HttpServletRequest request, HttpServletResponse response, NotFoundException notFoundException) throws IOException {
        // 404
        setResponseError(response, HttpServletResponse.SC_NOT_FOUND, String.format("Not found: %s", notFoundException.getMessage()));
    }

    @ExceptionHandler (value = {Exception.class})
    public void commence(HttpServletRequest request, HttpServletResponse response, Exception exception) throws IOException {
        // 500
        setResponseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, String.format("Internal Server Error: %s", exception.getMessage()));
    }

    private void setResponseError(HttpServletResponse response, int errorCode, String errorMessage) throws IOException{
        response.setStatus(errorCode);
        response.getWriter().write(errorMessage);
        response.getWriter().flush();
        response.getWriter().close();
    }
}

在您的安全配置中(我有可以通过OAuth2.0令牌访问的ResourceServer)

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.headers().frameOptions().sameOrigin();  // it is to fix issue with h2-console access

        http.csrf().disable()
            .authorizeRequests().antMatchers("/", "/callback", "/login**", "/webjars/**", "/error**").permitAll()
            .and()
            .authorizeRequests().antMatchers("/api/**").authenticated()
            .and()
            .authorizeRequests().antMatchers("/h2-console/**").permitAll()
            .and()
            .authorizeRequests().antMatchers("/swagger-ui.html").permitAll()
            .and()
            .authorizeRequests().antMatchers("/swagger-ui/**").permitAll()
            .and()
            .exceptionHandling().authenticationEntryPoint(new AppAuthenticationEntryPoint())
            .and()
            .logout().permitAll().logoutSuccessUrl("/");
    }

    @Bean
    public PrincipalExtractor getPrincipalExtractor(){
        return new KeyCloakUserInfoExtractorService();
    }

    @Autowired
    private ResourceServerTokenServices resourceServerTokenServices;
}
sycxhyv7

sycxhyv73#

我也遇到了同样的问题,并找到了解决办法。在扩展OncePerRequestFilter的自定义过滤器中,我最初使用的代码如下:

@Override
protected void doFilterInternal(@NonNull HttpServletRequest request,
                                @NonNull HttpServletResponse response,
                                @NonNull FilterChain filterChain) throws ServletException, IOException {
    // logic to check session expiration
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "message");
}

然后会被过滤掉,而服务器会发送一个403。但是,一旦我将代码切换到下面的代码,我就得到了正确的401错误。不知道为什么会这样。

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("message");

希望有帮助!

相关问题