java Spring security - 创建 403 Access denied 自定义响应

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48306302/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 10:01:56  来源:igfitidea点击:

Spring security - creating 403 Access denied custom response

javaspringrestspring-securityhttpresponse

提问by dudinii

I have a spring boot rest api with jwt authentication. The problem is i cannot get rid of default 403 Access Denied rest response which looks like this:

我有一个带有 jwt 身份验证的 spring boot rest api。问题是我无法摆脱默认的 403 Access Denied rest 响应,它看起来像这样:

{
    "timestamp": 1516206966541,
    "status": 403,
    "error": "Forbidden",
    "message": "Access Denied",
    "path": "/api/items/2"
}

I created custom AccessDeniedHandler:

我创建了自定义 AccessDeniedHandler:

public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest req,
                       HttpServletResponse res,
                       AccessDeniedException accessDeniedException) throws IOException, ServletException {



        ObjectMapper mapper = new ObjectMapper();
        res.setContentType("application/json;charset=UTF-8");
        res.setStatus(403);
        res.getWriter().write(mapper.writeValueAsString(new JsonResponse()
                .add("timestamp", System.currentTimeMillis())
                .add("status", 403)
                .add("message", "Access denied")));
    }
}

and added it to WebConfig class

并将其添加到 WebConfig 类

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    private UserDetailsService userDetailsService;
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.NEVER)
                .and()
                    .csrf().disable()
                    .authorizeRequests()
                    .antMatchers(HttpMethod.POST, REGISTER_URL).permitAll()
                    .anyRequest().authenticated()
                .and()
                    .exceptionHandling().accessDeniedHandler(accessDeniedHandler())
                .and()
                    .addFilter(new JWTAuthenticationFilter(authenticationManager(), tokenProvider()))
                    .addFilter(new JWTAuthorizationFilter(authenticationManager(), tokenProvider()));

    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Bean
    public TokenProvider tokenProvider(){
        return new TokenProvider();
    }

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

Despite this i'm still getting the default Access Denied response. When debugging i realized that the handlemethod from custom handler isn't even called. What is the case here?

尽管如此,我仍然收到默认的拒绝访问响应。调试时我意识到handle来自自定义处理程序的方法甚至没有被调用。这是什么情况?

采纳答案by dudinii

I think i resolved the problem. Instead of creating an implementation of AccessDeniedHandler i had to create a custom AuthenticationEntryPoint and set it in exception handling.

我想我解决了这个问题。我没有创建 AccessDeniedHandler 的实现,而是创建了一个自定义的 AuthenticationEntryPoint 并将其设置在异常处理中。

WebConfig now looks like this:

WebConfig 现在看起来像这样:

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    private UserDetailsService userDetailsService;
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                    .csrf().disable()
                    .authorizeRequests()
                    .antMatchers(HttpMethod.POST, REGISTER_URL).permitAll()
                    .anyRequest().authenticated()
                .and()
                    .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
                .and()
                    .addFilter(new JWTAuthenticationFilter(authenticationManager(), tokenProvider()))
                    .addFilter(new JWTAuthorizationFilter(authenticationManager(), tokenProvider()));

    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Bean
    public TokenProvider tokenProvider(){
        return new TokenProvider();
    }

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint(){
        return new CustomAuthenticationEntryPoint();
    }
}

and the CustomAuthenticationEntryPoint:

和 CustomAuthenticationEntryPoint:

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest req, HttpServletResponse res, AuthenticationException authException) throws IOException, ServletException {
        res.setContentType("application/json;charset=UTF-8");
        res.setStatus(403);
        res.getWriter().write(JsonBuilder //my util class for creating json strings
                .put("timestamp", DateGenerator.getDate())
                .put("status", 403)
                .put("message", "Access denied")
                .build());
    }
}

Now everything works as i wanted.

现在一切都如我所愿。

回答by Amit Samuel

I have the same problem & tried to resolve as per the right answer, but it doesn't solve the issue. The best way to handle this is to implement custom access denied handler. AuthenticationEntryPoint implementation is best to handle 401, UNAUTHORIZED access and AccessDeniedHandler implementation is there for 403, FORBIDDEN access.

我有同样的问题并试图按照正确的答案解决,但它没有解决问题。处理此问题的最佳方法是实现自定义访问拒绝处理程序。AuthenticationEntryPoint 实现最好处理 401、UNAUTHORIZED 访问和 AccessDeniedHandler 实现用于 403、FORBIDDEN 访问。

Override AccessDeniedHandler's method in your implementation class as:

将实现类中的 AccessDeniedHandler 方法重写为:

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, 
AccessDeniedException accessDeniedException) throws IOException, ServletException {
    response.getWriter().write("Access Denied... Forbidden");
}

And add this custom access denied handler in your security config like this:

并在您的安全配置中添加这个自定义访问被拒绝处理程序,如下所示:

.exceptionHandling()     
.authenticationEntryPoint(authenticationEntryPoint())
.accessDeniedHandler(accessDeniedHandler())

回答by Sachin Kumar

Try this

试试这个

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.NEVER)
                .and()
                    .csrf().disable()
                    .authorizeRequests()
                    .antMatchers(HttpMethod.POST, REGISTER_URL).permitAll()
                    .anyRequest().authenticated()

                 .and().exceptionHandling().accessDeniedPage("/view/notAuth")
                .and()
                    .addFilter(new JWTAuthenticationFilter(authenticationManager(), tokenProvider()))
                    .addFilter(new JWTAuthorizationFilter(authenticationManager(), tokenProvider()));

    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Bean
    public TokenProvider tokenProvider(){
        return new TokenProvider();
    }

And make this configuration class for view page

并为视图页面制作这个配置类

import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;


@Configuration
public class ViewRegistryConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/view/notAuth").setViewName("notAuth");
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // TODO Auto-generated method stub

    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        // TODO Auto-generated method stub

    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        // TODO Auto-generated method stub

    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // TODO Auto-generated method stub

    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        // TODO Auto-generated method stub

    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // TODO Auto-generated method stub

    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // TODO Auto-generated method stub

    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        // TODO Auto-generated method stub

    }

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        // TODO Auto-generated method stub

    }

    @Override
    public Validator getValidator() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        // TODO Auto-generated method stub
        return null;
    }



}

回答by Joe Grandja

Here is a minimal security configuration that demonstrates a custom AccessDeniedHandlergets called on access denied (403) scenarios:

这是一个最低限度的安全配置,它演示了AccessDeniedHandler在访问被拒绝 (403) 情况下调用的自定义获取:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/css/**", "/index").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .and()
            .formLogin()
                .and()
            .exceptionHandling()
                .accessDeniedHandler((request, response, accessDeniedException) -> {
                    AccessDeniedHandler defaultAccessDeniedHandler = new AccessDeniedHandlerImpl();
                    defaultAccessDeniedHandler.handle(request, response, accessDeniedException);
                });
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER"))
                .withUser(User.withDefaultPasswordEncoder().username("admin").password("password").roles("ADMIN"));
    }
}

Steps to reproduce:

重现步骤:

  1. Login with user/password
  2. Try to access http://localhost:8080/user/index- access granted
  3. Try to access http://localhost:8080/admin/index- access denied and custom AccessDeniedHandlergets called
  1. 登录 user/password
  2. 尝试访问http://localhost:8080/user/index- 授予访问权限
  3. 尝试访问http://localhost:8080/admin/index- 访问被拒绝并AccessDeniedHandler调用自定义

回答by John Humphreys - w00te

According to this:

根据这个:

http://www.baeldung.com/spring-security-custom-access-denied-page

http://www.baeldung.com/spring-security-custom-access-denied-page

You also needed to add:

您还需要添加:

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

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

guessing that goes in "configure".

猜测进入“配置”。