java - 如何处理 UsernameNotFoundException spring security

标签 java spring spring-boot exception spring-security

如何处理UsernameNotFoundException

在 Spring Security 中,当找不到用户名时,UserDetailsS​​ervice 实现会抛出 UsernameNotFoundException。例如这样:

   @Override
   @Transactional
   public UserDetails loadUserByUsername(java.lang.String username) throws UsernameNotFoundException {
       logger.info("Load user by username: {}", username);
       User user = userRepository.findUserByUsername(username).orElseThrow(
                   () -> new UsernameNotFoundException("User Not Found with -> username or email: " + username));

       return UserPrinciple.build(user);
   }

我想构建一个自定义的“用户未找到 REST 响应”。 我应该如何捕获/处理这个异常?我在 WebSecurityConfigurerAdapter 实现处理程序中实现了一个处理程序方法:

  private static void handleException(HttpServletRequest req, HttpServletResponse rsp, AuthenticationException e)
           throws IOException {
       PrintWriter writer = rsp.getWriter();
       writer.println(new ObjectMapper().writeValueAsString(new AuthResponse("", null, null, null, null,
               "Authentication failed.", false)));
       rsp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
   }

但是这个方法应该等待 AuthenticationException 异常,并且在运行时异常的类型是 java.lang.NullPointerException 所以我无法进行强制转换或检索初始的UsernameNotFoundException

如有任何建议,我们将不胜感激。 很多很多的问候:)。

最佳答案

安全层位于 Controller 和 @ControllerAdvice 中的任何内容之前。 因此@ControllerAdviceUsernameNotFoundException 起不再是一个选项它是 AuthenticationException 的子类在身份验证期间抛出,使您的异常处理程序位于 @ControllerAdvice无法到达。

您只能使用 @ControllerAdviceResponseEntityExceptionHandler如果你扔UsernameNotFoundException Controller 内部或从 Controller 引用的任何其他 bean。

这是我的建议 - 您实现 AuthenticationFailureHandler并将其与 AuthenticationFilter 一起使用您用于安全配置的。 Spring Boot Security 附带了大约 4 个用于安全相关问题的处理程序接口(interface)

  1. AccessDeniedHandler - 这可以处理用户没有所需角色等问题。
  2. AuthenticationEntryPoint - 这可以处理诸如用户尝试在没有适当身份验证元素的情况下访问资源等问题。

  3. AuthenticationFailureHandler - 这可以处理诸如找不到用户时(即 UsernameNotFoundException )或身份验证提供程序内部引发的其他异常等问题。事实上,这处理了 AccessDeniedException 未处理的其他身份验证异常。和AuthenticationEntryPoint .

  4. AuthenticationSuccessHandler - 这有助于在用户成功通过身份验证后执行重定向等操作。

请参阅以下示例片段,了解所有 4 个接口(interface)的实现。请根据您的喜好定制这些。

  1. AccessDeniedHandler实现
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;

@Component
public class RestAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {

        Map<String,Object> response = new HashMap<>();
        response.put("status","34");
        response.put("message","unauthorized api access");

        //httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        OutputStream out = httpServletResponse.getOutputStream();
        ObjectMapper mapper = new ObjectMapper();
        mapper.writerWithDefaultPrettyPrinter().writeValue(out,response);
        //mapper.writeValue(out, response);

        out.flush();
    }
}
  • AuthenticationEntryPoint实现
  • import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.http.HttpStatus;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.AuthenticationEntryPoint;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.OutputStream;
    
    @Component
    public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
        @Override
        public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
    
            Map<String,Object> response = new HashMap<>();
            response.put("status","34");
            response.put("message","unauthorized access");
            httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            OutputStream out = httpServletResponse.getOutputStream();
            ObjectMapper mapper = new ObjectMapper();
            mapper.writerWithDefaultPrettyPrinter().writeValue(out, response);
            out.flush();
        }
    }
    
  • AuthenticationFailureHandler实现
  • package com.ibiller.webservices.security;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.http.HttpStatus;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.OutputStream;
    
    
    @Component
    public class RestAuthenticationFailureHandler implements AuthenticationFailureHandler
    {
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse httpServletResponse,
                                            AuthenticationException ex) throws IOException, ServletException
        {
    
            Map<String,Object> response = new HashMap<>();
            response.put("status","34");
            response.put("message","unauthorized access");
    
            httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            OutputStream out = httpServletResponse.getOutputStream();
            ObjectMapper mapper = new ObjectMapper();
            mapper.writerWithDefaultPrettyPrinter().writeValue(out, response);
            out.flush();
        }
    }
    
  • AuthenticationSuccessHandler实现
  • import org.springframework.security.core.Authentication;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Component
    public class RestSuccessHandler implements AuthenticationSuccessHandler {
    
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
            Set<String> roles = 
              AuthorityUtils.authorityListToSet(authentication.getAuthorities());
            if (roles.contains("ROLE_ADMIN")) {
                //do something
            }
    
        }
    }
    

    这是扩展 WebSecurityConfigurerAdapter 的安全配置将一切连接在一起。

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpStatus;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.builders.WebSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.web.AuthenticationEntryPoint;
    import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
    import org.springframework.security.web.authentication.HttpStatusEntryPoint;
    import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
    import org.springframework.security.web.util.matcher.OrRequestMatcher;
    import org.springframework.security.web.util.matcher.RequestMatcher;
    
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(
            prePostEnabled = true,
            securedEnabled = true,
            jsr250Enabled = true)
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    
        private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
                new AntPathRequestMatcher("/v1/**"),new AntPathRequestMatcher("/admin/**")
        );
    
        AuthenticationProvider provider;
    
        public SecurityConfiguration(final AuthenticationProvider authenticationProvider) {
            super();
            this.provider=authenticationProvider;
        }
    
        @Override
        protected void configure(final AuthenticationManagerBuilder auth) {
            auth.authenticationProvider(provider);
        }
    
    
        @Override
        public void configure(final WebSecurity webSecurity) {
            webSecurity.ignoring().antMatchers("/info/**");//url that will be ignored
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .exceptionHandling()
                    .accessDeniedHandler(accessDeniedHandler())
                   .authenticationEntryPoint(authenticationEntryPoint())
                    .and()
                    .authenticationProvider(provider)
                    .addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class)
                    .authorizeRequests()
                    .antMatchers("/v1/**").hasRole("API")
                    .antMatchers("/admin/**").hasAnyRole("SUPER_ADMIN","ADMIN")
                    .and()
                    .csrf().disable()
                    .formLogin().disable()
                    .httpBasic().disable()
                    .logout().disable();
        }
    
        @Bean
          AuthenticationFilter authenticationFilter() throws Exception {
            final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
            filter.setAuthenticationManager(authenticationManager());
            filter.setAuthenticationSuccessHandler(successHandler());
            filter.setAuthenticationFailureHandler(authenticationFailureHandler());
            return filter;
        }
    
        @Bean
        RestAccessDeniedHandler accessDeniedHandler() {
            return new RestAccessDeniedHandler();
        }
    
        @Bean
        RestAuthenticationEntryPoint authenticationEntryPoint() {
            return new RestAuthenticationEntryPoint();
        }
    
        @Bean
        RestAuthenticationFailureHandler authenticationFailureHandler(){
            return new RestAuthenticationFailureHandler();
        }
    
        @Bean
        RestSuccessHandler successHandler(){
            return new RestSuccessHandler();
        }
    }
    

    关于java - 如何处理 UsernameNotFoundException spring security,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59417122/

    相关文章:

    java - Swagger API 操作排序

    java - Zebra EM 220,使用 Android 打印 Code 128 条码

    java - 我们如何计算小于 1 的数字的对数值...?使用java

    java - 如何读取 .txt 文件并在 TextView 中显示

    java - 为什么 servlet 名称附加到请求的 map url 后面?是否可以有没有 Servlet 名称前缀的 URL?

    java - 找不到 SecurityMockMvcConfigurers

    java - 如何在不使用准备好的语句的情况下清理 SQL

    java - 如何在 Spring Boot Rest 中正确发布 json 对象

    spring - 如何使用 Junit 测试 Spring Controller 方法

    spring-boot - HikariCP 中的 initializationFailTimeout 属性