Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AccessDeniedHandler and AuthenticationEntryPoint does not work Because of the global exception handler #6908

Closed
allurx opened this issue May 28, 2019 · 18 comments
Assignees
Labels
status: invalid An issue that we don't feel is valid

Comments

@allurx
Copy link

allurx commented May 28, 2019

Summary

AccessDeniedHandler and AuthenticationEntryPoint do not work because the global exception handler is defined

Actual Behavior

When the request has an AuthenticationException or an AccessDeniedException, it does not enter my custom AccessDeniedHandler and AuthenticationEntryPoint.

1、Access to protected resources
2、Console prints AccessDeniedException,it does not enter my custom AccessDeniedHandler
3、I found it into my custom global exception handler.

Expected Behavior

enter my custom AccessDeniedHandler or AuthenticationEntryPoint

Configuration

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> exception(Exception exception) throws Exception {
        log.error(exception.getMessage(), exception);
        return ResponseEntity.ok("系统繁忙,请稍后再试!");
    }

}
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;
    private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    private StaticHeadersWriter staticHeadersWriter;

    {
        List<Header> headers = new ArrayList<>();
        headers.add(new Header("Access-Control-Allow-Origin", "*"));
        headers.add(new Header("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACES"));
        headers.add(new Header("Access-Control-Allow-Headers", Security.TOKEN));
        staticHeadersWriter = new StaticHeadersWriter(headers);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/swagger-ui.html",
                        "/swagger-resources/**",
                        "/webjars/**",
                        "/v2/api-docs")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .addFilterAt(new UsernamePasswordAuthenticationFilter(authenticationManager()), org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(new JwtAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class)
                .addFilterAfter(new OptionsRequestFilter(), HeaderWriterFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(new CustomizedAuthenticationEntryPoint())
                .accessDeniedHandler(new CustomizedAccessDeniedHandler())
                .and()
                .headers()
                .addHeaderWriter(staticHeadersWriter)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf()
                .disable()
                .logout()
                .disable();
    }

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

}

Version

spring-boot-starter-security 2.1.5.RELEASE

Sample

spring-security-demo

Additional

From the log printed by the console, I guess if the AccessDeniedException thrown by the MethodSecurityInterceptor when calling the beforeInvocation method is caught by the global exception handler, causing the superior ExceptionTranslationFilter to not catch the exception.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 28, 2019
@citygs
Copy link

citygs commented Aug 26, 2019

I got the same problem,how to fix this bug?

@allurx
Copy link
Author

allurx commented Aug 26, 2019

@citygs

Currently I can only throw the exception caught by the global exception handler up, and then AccessDeniedHandler and AuthenticationEntryPoint can catch this exception. You can check out the handler in spring-security-demo I wrote earlier.GlobalExceptionHandler

@rwinch
Copy link
Member

rwinch commented Sep 19, 2019

There really isn't anything we can do on our side. It might be that users actually want to catch the exception and process it. Instead we recommend that if you get a Security related exception to re-throw it.

@rwinch rwinch added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged labels Sep 19, 2019
@rwinch rwinch self-assigned this Sep 19, 2019
@allurx
Copy link
Author

allurx commented Sep 20, 2019

Thank you for your reply, my current practice is to re-throw spring-security related exceptions.

@rwinch rwinch removed their assignment Nov 6, 2019
@aboutZZ
Copy link

aboutZZ commented Apr 14, 2020

Same prolem here

@LeoSong121
Copy link

LeoSong121 commented May 14, 2020

Similar questions. Neither call AuthenticationEntryPoint, nor call global exception handler. But get:
{
"timestamp": 1589452582946,
"status": 500,
"error": "Internal Server Error",
"message": "invalid token",
"path": "/auth/xxx"
}

@caiqichang
Copy link

i got this problem if using .antMatcher() .
without any .antMatcher() , everything work well !

@rwinch rwinch self-assigned this Dec 3, 2020
@rwinch rwinch closed this as completed Dec 3, 2020
@rwinch
Copy link
Member

rwinch commented Dec 3, 2020

Closing. As mentioned before, there isn't anything we can do. Users may want to be handling the Security related exceptions.

@zhuzhiyun
Copy link

Similar questions. Neither call AuthenticationEntryPoint, nor call global exception handler. But get:
{
"timestamp": 1589452582946,
"status": 500,
"error": "Internal Server Error",
"message": "invalid token",
"path": "/auth/xxx"
}

I got this problem too,how to fix it?

@allurx
Copy link
Author

allurx commented Dec 20, 2020

@zhuzhiyun Catch AccessDeniedException and AuthenticationException and throw them, AccessDeniedHandler and AuthenticationEntryPoint will work normally.see GlobalExceptionHandler

@aboutZZ
Copy link

aboutZZ commented Apr 12, 2021

Similar questions. Neither call AuthenticationEntryPoint, nor call global exception handler. But get:
{
"timestamp": 1589452582946,
"status": 500,
"error": "Internal Server Error",
"message": "invalid token",
"path": "/auth/xxx"
}

I got this problem too,how to fix it?

Hi, have you fixed this problem? Catch AuthenticationException then rethrow them, I'm still getting this:

{
    "timestamp": "2021-04-12 15:52:07",
    "status": 500,
    "error": "Internal Server Error",
    "message": "",
    "path": "/api/xxxx/xxxx"
}

@allurx
Copy link
Author

allurx commented Apr 12, 2021

@aboutZZ 我之前写的spring-security-demo,定义了一个全局异常捕获器捕获spring-security相关的异常,然后继续向上抛出就能进入到AccessDeniedHandler和AuthenticationEntryPoint中了,我怀疑你没有将这两个类配置进去,你看一下这个配置类WebSecurityConfig中的configure方法

@aboutZZ
Copy link

aboutZZ commented Apr 12, 2021

@aboutZZ 我之前写的spring-security-demo,定义了一个全局异常捕获器捕获spring-security相关的异常,然后继续向上抛出就能进入到AccessDeniedHandler和AuthenticationEntryPoint中了,我怀疑你没有将这两个类配置进去,你看一下这个配置类WebSecurityConfig中的configure方法

全局异常捕获器捕获后再抛出
下面是配置类: 在自定义授权异常处理处配置了AuthenticationEntryPoint

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // We don't need CSRF for this example
        httpSecurity.csrf().disable().logout().disable()

                // Relax CSRF on the WebSocket due to needing direct access from apps
                //.csrf().ignoringAntMatchers("/ws/**").and()

                // dont authenticate this particular request
                .authorizeRequests()
                // OPTIONS请求全部放行
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/api/user/login").permitAll()
                .antMatchers("/error").permitAll()
                // all other requests need to be authenticated
                .anyRequest().authenticated()

                // make sure we use stateless session; session won't be used to
                // store user's state.
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // Add a filter to validate the tokens with every request
                .and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                // 自定义授权异常处理
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler)
                // No cache
                .and().headers().cacheControl()
        ;

写法应该没问题啊

@allurx
Copy link
Author

allurx commented Apr 12, 2021

@aboutZZ 你在ExceptionTranslationFilter这个过滤器的doFilter方法中打断点看看呢,spring-security中的异常都会经过这个过滤器的,还有把异常堆栈贴一下,看看经过spring-security的哪几个过滤器了。

@aboutZZ
Copy link

aboutZZ commented Apr 12, 2021

@aboutZZ 你在ExceptionTranslationFilter这个过滤器的doFilter方法中打断点看看呢,spring-security中的异常都会经过这个过滤器的,还有把异常堆栈贴一下,看看经过spring-security的哪几个过滤器了。

如下图, JwtRequestFilter是自定义的JWT认证过滤器
堆栈信息如下, 经过了以下几个过滤器

  • WebAsyncManagerIntegrationFilter
  • SecurityContextPersistenceFilter
  • HeaderWriterFilter
  • JwtRequestFilter

在经过JwtRequestFilter时抛出异常, 但是异常似乎被其中一个过滤器给处理了, ExceptionTranslationFilter中并未捕获到异常
debug发现被重定向到/error, 因此出现了那个默认的Json响应

截图_20210412183431.png

堆栈信息如下:


Cause: java.sql.SQLSyntaxErrorException: Unknown column 'mobile2' in 'field list'
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Unknown column 'mobile2' in 'field list'
	at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:239)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:88)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
	at com.sun.proxy.$Proxy142.selectOne(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:159)
	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:90)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
	at com.sun.proxy.$Proxy143.selectById(Unknown Source)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205)
	at com.sun.proxy.$Proxy144.selectById(Unknown Source)
	at com.baomidou.mybatisplus.extension.service.IService.getById(IService.java:201)
	at com.zbzl.service.impl.AppUserServiceImpl.lambda$getByIdUnSafely$1(AppUserServiceImpl.java:112)
	at com.zbzl.service.impl.AppUserServiceImpl$$Lambda$1708/000000000000000000.get(Unknown Source)
	at java.base/java.util.Optional.orElseGet(Optional.java:369)
	at com.zbzl.service.impl.AppUserServiceImpl.getByIdUnSafely(AppUserServiceImpl.java:111)
	at com.zbzl.service.impl.AppUserServiceImpl$$FastClassBySpringCGLIB$$8981be77.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:687)
	at com.zbzl.service.impl.AppUserServiceImpl$$EnhancerBySpringCGLIB$$c53fb596.getByIdUnSafely(<generated>)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205)
	at com.sun.proxy.$Proxy109.getByIdUnSafely(Unknown Source)
	at com.zbzl.config.security.JWTUserDetailService.loadUserByUsername(JWTUserDetailService.java:27)
	at com.zbzl.config.security.JwtRequestFilter.doFilterInternal(JwtRequestFilter.java:87)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:346)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:887)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1684)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:836)

通过日志发现被重定向到/error:

2021-04-12 18:37:08.842 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /api/live/pull-stream/token?channel=devCOLLEGE_2356 at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2021-04-12 18:37:08.843 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /api/live/pull-stream/token?channel=devCOLLEGE_2356 at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2021-04-12 18:37:08.843 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /api/live/pull-stream/token?channel=devCOLLEGE_2356 at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2021-04-12 18:37:08.843 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /api/live/pull-stream/token?channel=devCOLLEGE_2356 at position 4 of 11 in additional filter chain; firing Filter: 'JwtRequestFilter'
2021-04-12 18:37:08.913 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@e7b35617
2021-04-12 18:37:08.913 DEBUG 14568 --- [nio-8080-exec-6] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2021-04-12 18:37:08.915 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2021-04-12 18:37:08.915 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2021-04-12 18:37:08.915 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2021-04-12 18:37:08.915 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 4 of 11 in additional filter chain; firing Filter: 'JwtRequestFilter'
2021-04-12 18:37:08.915 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 5 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
2021-04-12 18:37:08.915 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2021-04-12 18:37:08.915 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2021-04-12 18:37:08.915 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2021-04-12 18:37:08.915 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@4f09adb6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2021-04-12 18:37:08.916 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
2021-04-12 18:37:08.916 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2021-04-12 18:37:10.526 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2021-04-12 18:37:10.526 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /error' doesn't match 'OPTIONS /**'
2021-04-12 18:37:10.526 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/api/user/login'
2021-04-12 18:37:10.526 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/error'
2021-04-12 18:37:10.526 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /error?channel=devCOLLEGE_2356; Attributes: [permitAll]
2021-04-12 18:37:10.526 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@4f09adb6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2021-04-12 18:37:10.527 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@c15ad0f6, returned: 1
2021-04-12 18:37:10.527 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
2021-04-12 18:37:10.527 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
2021-04-12 18:37:10.527 DEBUG 14568 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy        : /error?channel=devCOLLEGE_2356 reached end of additional filter chain; proceeding with original chain
2021-04-12 18:37:10.530 DEBUG 14568 --- [nio-8080-exec-6] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2021-04-12 18:37:10.530 DEBUG 14568 --- [nio-8080-exec-6] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

@allurx
Copy link
Author

allurx commented Apr 12, 2021

@aboutZZ 你把配置中的antMatchers("/error").permitAll()去掉再试试呢

@aboutZZ
Copy link

aboutZZ commented Apr 13, 2021

@aboutZZ 你把配置中的antMatchers("/error").permitAll()去掉再试试呢

Problem solved:

  • 在认证过程中, 对可能异常的地方 (例如查询数据库) 捕获RuntimeException 然后抛出 InternalAuthenticationServiceException
final AppUserDetails userDetails;
try {
    userDetails = (AppUserDetails) this.jwtUserDetailService.loadUserByUsername(username);
} catch (RuntimeException e) {
    // not handle AuthenticationException
    if (e instanceof AuthenticationException) throw e;
    throw new InternalAuthenticationServiceException("Error while executing loadUserByUsername", e);
}
  • WebSecurityConfig中,自定义的JWT 认证过滤器放在AnonymousAuthenticationFilter之前
    addFilterBefore(jwtRequestFilter, AnonymousAuthenticationFilter.class)

  • 自定义控制器类, 实现 org.springframework.boot.web.servlet.error.ErrorController接口来自定义error page.
    Spring boot 2.3+中, 注意此处有坑, 尽管getErrorPath方法已被弃用, 你仍然要实现ErrorController接口, 否则不生效, 这应该是个BUG. 你的ErrorPath应当通过server.error.path配置.

@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ErrorController implements org.springframework.boot.web.servlet.error.ErrorController  {
    @RequestMapping("/error")
    public ResponseEntity<?> handleError(HttpServletRequest request) {
        Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);

        int statusCode = (int) status;
        final HttpStatus httpStatus = HttpStatus.valueOf(statusCode);
        final String errMsg;

        switch (httpStatus) {
            case UNAUTHORIZED:
                errMsg = "身份认证信息过期,请重新登录";
                break;
            case INTERNAL_SERVER_ERROR:
                errMsg = "服务器内部错误";
                break;
            case FORBIDDEN:
                errMsg = "无权限访问";
                break;
            ... ... 省略代码
            default:
                errMsg = httpStatus.getReasonPhrase();
        }

        return ResponseEntity.status(httpStatus).body(new AppErrorResponse(errMsg));
    }

    @Override
    public String getErrorPath() {
        return "/error";
    }
}

@allurx
Copy link
Author

allurx commented Apr 13, 2021

@aboutZZ 👋👋👋

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

8 participants