当前位置: 首页 > 知识库问答 >
问题:

用于预控制器过滤器的ExceptionHandler(Spring Security)

卜和悌
2023-03-14

我有一个关于Spring Security级别上的异常的问题。

public class DelegateRequestMatchingFilter extends RequestHeaderAuthenticationFilter {

private RequestMatcher ignoredRequests;
public DelegateRequestMatchingFilter(RequestMatcher matcher) {
    super();
    super.setPrincipalRequestHeader("SM_USER");
    this.ignoredRequests = matcher;
}   

@Override
public void doFilter(ServletRequest req, ServletResponse resp,FilterChain chain) throws IOException, ServletException {

     HttpServletRequest httpReq = (HttpServletRequest) req;      
     if(ignoredRequests.matches(httpReq)) {
         chain.doFilter(req,resp);
     } else {
         super.doFilter(req,resp,chain);//<-- throws exception
     }       
}   
}
 @ControllerAdvice
public class ExceptionResolver extends AbstractHandlerExceptionResolver{


@Override
protected ModelAndView doResolveException(HttpServletRequest request,
        HttpServletResponse responce, Object handler, Exception exception) {

    ModelAndView toReturn = new ModelAndView();
    toReturn.setView(new MappingJackson2JsonView());
    toReturn.addObject("message", exception.getMessage());
    toReturn.addObject("exceptionClass", exception.getClass().getCanonicalName());
    return toReturn;
}

@ExceptionHandler(PreAuthenticatedCredentialsNotFoundException.class)       
public ResponseEntity<Result> handleBindException(PreAuthenticatedCredentialsNotFoundException e) {

    Result result = new Result("the identification header is missing");
    result.setException(PreAuthenticatedCredentialsNotFoundException.class);
    ResponseEntity<Result> response = new ResponseEntity<Result>(result, HttpStatus.FORBIDDEN);
    return response;
}
}

安全配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

private UserDetailsService userDetailsService;  
private PreAuthenticatedAuthenticationProvider preAuthenticatedProvider;



@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

    Map<String, List<GrantedAuthority>> rolesAuthorities = getRolesWithAutorities();

    userDetailsService = new CustomUserDetailsService(...);
    UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> wrapper = 
            new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>(userDetailsService);
    preAuthenticatedProvider = new PreAuthenticatedAuthenticationProvider();
    preAuthenticatedProvider.setPreAuthenticatedUserDetailsService(wrapper);
    auth.authenticationProvider(preAuthenticatedProvider);
}   

@Bean
public SmUserFailureHandler smUserFailureHandler(){
    return new SmUserFailureHandler();
}

@Bean
public AccessDeniedHandler accessDeniedHandler(){
    AccessDeniedHandlerImpl handler = new AccessDeniedHandlerImpl();
    handler.setErrorPage("/errorPage/");        
    return handler;     
}

public RequestHeaderAuthenticationFilter siteMinderFilter() throws Exception
{

    RequestHeaderAuthenticationFilter siteMinderFilter = new DelegateRequestMatchingFilter(
            new OrRequestMatcher(
            new AntPathRequestMatcher("/uriToSkip/")));

    siteMinderFilter.setPrincipalRequestHeader("SM_USER");
    siteMinderFilter.setAuthenticationManager(authenticationManager());   
    return siteMinderFilter;
}

@Override
protected void configure(HttpSecurity http) throws Exception {  

 //...   
 http.addFilter(siteMinderFilter());
 ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
  //adding methods... 
  http = registry.and();

  http.csrf().disable();
  http.headers().cacheControl().disable();
  http
  .sessionManagement()
      .sessionCreationPolicy(SessionCreationPolicy.NEVER);    
}
}

如果我根据答案更改代码,即使SM_USEr标头存在且正确,也会出现此异常

org.springframework.security.authentication.InsufficientAuthenticationException: Full authentication is required to access this resource
at org.springframework.security.web.access.ExceptionTranslationFilter.handleSpringSecurityException(ExceptionTranslationFilter.java:177)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:133)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:168)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:48)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:205)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:497)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:310)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
at java.lang.Thread.run(Thread.java:745)

共有1个答案

太叔岳
2023-03-14

首先,您应该在ExceptionTranslationFilter之后添加筛选器,此筛选器将在捕获AccessDeniedExceptionAuthenticationException时处理它们。

http.addFilterAfter(siteMinderFilter(), ExceptionTranslationFilter.class);

接下来,您需要注入AuthenticationEntryPoint来处理AuthenticationException,已经存在一些AuthenticationEntryPoint,您可以使用它,如HttpStatusEntryPointLoginurlAuthenticationEntryPoint、...;

如果希望返回JSON响应,则需要实现自己的authenticationentrypoint,例如:

public class ExampleAuthenticationEntryPoint implements AuthenticationEntryPoint {

    private static final Logger logger = LoggerFactory.getLogger(ExampleAuthenticationEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        logger.error(authException.getMessage(), authException);
        if (!response.isCommitted()) {
            Map<String, Object> responseMsg = new HashMap<String, Object>();
            responseMsg.put("message", authException.getMessage());
            ObjectMapper mapper = new ObjectMapper();
            try {
                response.setContentType("application/json;charset=utf-8");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                PrintWriter writer = response.getWriter();
                writer.write(mapper.writeValueAsString(responseMsg));
                writer.flush();
                writer.close();
            } catch (IOException e) {
                // ignore
                logger.error(e.getMessage(), e);
            }
        }
    }
}
http.exceptionHandling().authenticationEntryPoint(myEntryPoint());

@Bean
AuthenticationEntryPoint myEntryPoint() {
    return new ExampleAuthenticationEntryPoint();
}

/*
Or just return 401
AuthenticationEntryPoint myEntryPoint() {
    return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);
} 
*/
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(
                                "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);
            }
        }

更新2

Authentication currentUser = SecurityContextHolder.getContext()
            .getAuthentication();

    if (currentUser == null) {
        return true;
    }

    if (!checkForPrincipalChanges) {
        return false;
    }

    if(!principalChanged(request, currentUser)) {
        return false;
    }

CheckForPrincipalChanges的默认值是false,所以现在如果启用Anon筛选器,它将返回false并跳过RequestTheAderAuthenticationFilter中的do authentication,所以只需尝试do disableAnon筛选器。您需要学习Spring Security代码并通过调试对其进行研究。

 类似资料:
  • Template prefilters are PHP functions that your templates are ran through before they are compiled. This is good for preprocessing your templates to remove unwanted comments, keeping an eye on what pe

  • 问题内容: 我写了一个过滤器函数,它将根据您传递的参数返回数据。我希望控制器具有相同的功能。是否可以在控制器中重用过滤器功能? 到目前为止,这是我尝试过的: 问题答案: 将 $ filter 注入控制器 然后,无论您想在哪里使用该过滤器,都可以像这样使用它: 如果要将参数传递给该过滤器,请使用单独的括号进行处理: 您要过滤的数组在哪里,并且是用于过滤的对象。

  • void unregister_prefilter(string function_name) Use this to dynamically unregister a prefilter. 动态注销一个预过滤器(模板编译前执行)。

  • void register_prefilter(mixed function) Use this to dynamically register prefilters to run templates through before they are compiled. See template prefilters for more information on how to setup a pr

  • 问题内容: 我的过滤器很少 在我的项目中,要获得良好的结果,我必须在控制器中进行此过滤,而不是在视图中 我知道基本的语法,但我不知道如何在控制器中制作过滤器链,因此一切都将如模板中的示例一样工作。 我找到了一些解决方案,现在仅使用一个过滤器,但是应该可以用于许多;) 我在ng-repeat中使用此功能可以正常工作,但是我必须使用少量过滤器进行检查。 问题答案: 您只需重新过滤您从第一个过滤器返回的

  • 问题内容: 有谁知道如何编写一个servlet过滤器,该过滤器将在给定文件/内容类型的响应上设置缓存头?我有一个提供大量图像的应用程序,我想通过让浏览器缓存那些不经常更改的图像来减少托管它的带宽。理想情况下,我希望能够指定一种内容类型,并在内容类型匹配时让它设置适当的标题。 有人知道该怎么做吗?或者,甚至更好的是,他们愿意共享示例代码吗?谢谢! 问题答案: 在您的过滤器中有以下行: 响应包装如下所