简体   繁体   English

如何使用 Spring Security 自定义 403 Forbidden/Access Denied Page

[英]How to customize the 403 Forbidden/Access Denied Page using Spring Security

I'm a junior web developper and I need to customize the 403 error page using react js, I've found other projects implementing the AccessDeniedHandler interface but I don't know how to use it in my Security Configuration class.我是初级 web 开发人员,我需要使用 react js 自定义 403 错误页面,我发现其他项目实现了 AccessDeniedHandler 接口,但我不知道如何在我的安全配置 class 中使用它。

This is my CustomAccessDeniedHandler class:这是我的 CustomAccessDeniedHandler class:

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    private static Logger logger = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);

    @Override
    public void handle(HttpServletRequest httpServletRequest,
                       HttpServletResponse httpServletResponse,
                       AccessDeniedException e) throws IOException, ServletException {
        System.out.println("accessDenied");
        Authentication auth
                = SecurityContextHolder.getContext().getAuthentication();

        if (auth != null) {
            logger.info("User '" + auth.getName()
                    + "' attempted to access the protected URL: "
                    + httpServletRequest.getRequestURI());
        }

        httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/accessDenied");
    }
}

This is the Security Configuration class:这是安全配置 class:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    AppUserService userDetailsService;
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable()
                .exceptionHandling()
                .authenticationEntryPoint(new Http403ForbiddenEntryPoint() {})
                .and()
                .authenticationProvider(getProvider())
                .formLogin()
                .loginPage("/login")
                .successHandler(new AuthentificationLoginSuccessHandler())
                .failureHandler(new SimpleUrlAuthenticationFailureHandler())
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(new AuthentificationLogoutSuccessHandler())
                .invalidateHttpSession(true)
                .and()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers("/logout").permitAll()
                .antMatchers("/api/categories").hasAuthority("USER")
                .antMatchers("/api/createCategory").hasAuthority("ADMIN")
                .anyRequest().permitAll();
    }

    private class AuthentificationLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
                throws IOException, ServletException {
            response.setStatus(HttpServletResponse.SC_OK);
        }
    }

    private class AuthentificationLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                                    Authentication authentication) throws IOException, ServletException {
            response.setStatus(HttpServletResponse.SC_OK);
        }
    }

    @Bean
    public AuthenticationProvider getProvider() {
        AppAuthProvider provider = new AppAuthProvider();
        provider.setUserDetailsService(userDetailsService);
        return provider;
    }

    @Bean
    public AccessDeniedHandler accessDeniedHandler(){
        return new CustomAccessDeniedHandler();
    }
}

What you have to do is to create @Bean你要做的就是创建@Bean

@Bean
public AccessDeniedHandler accessDeniedHandler(){
    return new CustomAccessDeniedHandler();
}

which you already have and then add that handler in http security object like this:你已经有了,然后在 http security object 中添加该处理程序,如下所示:

http.csrf()
            .disable()
            .exceptionHandling()
            .authenticationEntryPoint(new Http403ForbiddenEntryPoint() {}) //remove this line or use Http401UnauthorizedEntryPoint instead
            .and()
            .authenticationProvider(getProvider())
            .formLogin()
            .loginPage("/login")
            .successHandler(new AuthentificationLoginSuccessHandler())
            .failureHandler(new SimpleUrlAuthenticationFailureHandler())
            .and()
            .logout()
            .logoutUrl("/logout")
            .logoutSuccessHandler(new AuthentificationLogoutSuccessHandler())
            .invalidateHttpSession(true)
            .and()
            .authorizeRequests()
            .antMatchers("/login").permitAll()
            .antMatchers("/logout").permitAll()
            .antMatchers("/api/categories").hasAuthority("USER")
            .antMatchers("/api/createCategory").hasAuthority("ADMIN")
            .anyRequest().permitAll()
            .and()
            .exceptionHandling().accessDeniedHandler(accessDeniedHandler());

As you can see you are missing:如您所见,您缺少:

.and().exceptionHandling().accessDeniedHandler(accessDeniedHandler());

Additional, remove额外的,删除

@Autowired
private AccessDeniedHandler accessDeniedHandler;

because you shouldn't autowire bean, you should create it with custom implementation.因为您不应该自动装配 bean,所以您应该使用自定义实现来创建它。

EDIT: If you have @RestControllerAdvice or @ControllerAdvice as global exception handler you should do following:编辑:如果您有@RestControllerAdvice@ControllerAdvice作为全局异常处理程序,您应该执行以下操作:

@ExceptionHandler(Exception.class)
public ResponseEntity<?> exception(Exception exception) throws Exception {
        if (exception instanceof AccessDeniedException) {
            throw exception;
        } 
...

then it should work because when you throw exception it will go to custom handler which we do what you wrote.那么它应该可以工作,因为当你抛出异常时,它会将 go 发送到自定义处理程序,我们会按照你写的去做。 Also you can debug ExceptionTranslationFilter method handleSpringSecurityException您也可以调试ExceptionTranslationFilter方法handleSpringSecurityException

code from ExceptionTranslationFilter来自ExceptionTranslationFilter的代码

private void handleSpringSecurityException(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain, RuntimeException exception)
            throws IOException, ServletException {
        if (exception instanceof AuthenticationException) {
            logger.debug(
                    "Authentication exception occurred; redirecting to authentication entry point",
                    exception);

            sendStartAuthentication(request, response, chain,
                    (AuthenticationException) exception);
        }
        else if (exception instanceof AccessDeniedException) {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {
                logger.debug(
                        "Access is denied (user is " + (authenticationTrustResolver.isAnonymous(authentication) ? "anonymous" : "not fully authenticated") + "); redirecting to authentication entry point",
                        exception);

                sendStartAuthentication(
                        request,
                        response,
                        chain,
                        new InsufficientAuthenticationException(
                            messages.getMessage(
                                "ExceptionTranslationFilter.insufficientAuthentication",
                                "Full authentication is required to access this resource")));
            }
            else {
                logger.debug(
                        "Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
                        exception);

                accessDeniedHandler.handle(request, response,
                        (AccessDeniedException) exception);
            }
        }
    }

where you can see that accessDeniedHandler.handle(request, response,(AccessDeniedException) exception);在那里你可以看到accessDeniedHandler.handle(request, response,(AccessDeniedException) exception); , in you case CustomAccessDeniedHandler , get called. ,在你的情况下CustomAccessDeniedHandler ,被调用。

I just tried it and it is working fine with (I have @ControllerAdvice as global exception handler)我刚刚尝试过它并且它工作正常(我有@ControllerAdvice作为全局异常处理程序)

EDIT2: You have to remove this line EDIT2:你必须删除这一行

.authenticationEntryPoint(new Http403ForbiddenEntryPoint() {})

from SecurityConfig , or change it to use Http401UnauthorizedEntryPoint instead.来自SecurityConfig ,或将其更改为使用Http401UnauthorizedEntryPoint This is the problem in your case.这是你的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM