java - Spring Boot 中永远不会调用 AuthenticationEntryPoint

标签 java spring-boot exception spring-security-oauth2

我有一个用于自定义 token 的 Spring Security 实现,我尝试了很多方法来实现身份验证异常的自定义响应,但我找不到解决方案,它从未被调用。

为了安全起见,我有这样的配置:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
            .sessionManagement()
            .sessionCreationPolicy(STATELESS)
            .and()
            .exceptionHandling().authenticationEntryPoint(new AuthenticationExceptionHandler())
            // this entry point handles when you request a protected page and you are not yet
            // authenticated
            .defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
            .and()
            .authenticationProvider(provider)
            .addFilterBefore(restAuthenticationFilter(), AnonymousAuthenticationFilter.class)
            .authorizeRequests()
            .requestMatchers(PROTECTED_URLS)
            .authenticated()
            .and()
            .csrf().disable()
            .formLogin().disable()
            .httpBasic().disable()
            .logout().disable()
    ;
}

这是 TokenAuthenticationProvider:

@Override
protected UserDetails retrieveUser(final String username, final 
UsernamePasswordAuthenticationToken authentication) throws 
AuthenticationException {
    final String token = (String) authentication.getCredentials();
    logger.info("Retrieving user details from the token.");
    FirebaseToken decodedToken;
    UserAuth user = new UserAuth();
    try {
        decodedToken = FirebaseAuth.getInstance().verifyIdToken(token);
        user.setId(decodedToken.getUid());
        user.setEmail(decodedToken.getEmail());
        user.setName(decodedToken.getName());
        user.setClaims(decodedToken.getClaims());
    } catch (FirebaseAuthException e) {
        e.printStackTrace();
        throw new CredentialsExpiredException("Fail getting the idUser 
        maybe token expired.");
    }

    return user;
}

当 Firebase token 无效时,我会从 org.springframework.security.authentication 抛出 CredentialsExpiredException,但我仍然收到此答案:

{
"timestamp": "2019-01-16T16:51:54.696+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/employer"
}

这是 AuthenticationEntryPoint:

@Component
public class AuthenticationExceptionHandler implements 
AuthenticationEntryPoint, Serializable {

@Override
public void commence(HttpServletRequest request, HttpServletResponse 
response, AuthenticationException authException) throws IOException, 
ServletException {

    response.setStatus(HttpStatus.UNAUTHORIZED.value());

    Map<String, Object> error = new HashMap<>();
    error.put("domain", "global");
    error.put("reason", "required");
    error.put("message", "Invalid credentials.");
    ArrayList<Map<String, Object>> errorsList = new ArrayList<>();
    errorsList.add(error);
    Map<String, Object> errors = new HashMap<>();
    errors.put("errors", errorsList);
    errors.put("code", 401);
    errors.put("message", "Invalid credentials.");

    Map<String, Object> data = new HashMap<>();
    data.put("error", errors);

    ObjectMapper mapper = new ObjectMapper();
    String responseMsg = mapper.writeValueAsString(data);
    response.getWriter().write(responseMsg);
   }
}

最佳答案

我解决了这个问题,实现了一个 AuthenticationFailureHandler,该处理程序由在 AbstractAuthenticationProcessingFilter 中实现的 unsuccessfulAuthentication 方法调用...这是配置代码:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
            .sessionManagement()
            .sessionCreationPolicy(STATELESS)
            .and()
            .exceptionHandling()
            // this entry point handles when you request a protected page and you are not yet
            // authenticated
            .defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
            .and()
            .authenticationProvider(provider)
            .addFilterBefore(restAuthenticationFilter(), AnonymousAuthenticationFilter.class)
            .authorizeRequests()
            .requestMatchers(PROTECTED_URLS)
            .authenticated()
            .and()
            .csrf().disable()
            .formLogin().disable()
            .httpBasic().disable()
            .logout().disable()
    ;
}


@Bean
TokenAuthenticationFilter restAuthenticationFilter() throws Exception {
    final TokenAuthenticationFilter filter = new TokenAuthenticationFilter(PROTECTED_URLS);
    filter.setAuthenticationManager(authenticationManager());
    filter.setAuthenticationSuccessHandler(successHandler());
    filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
    return filter;
}

这是 AuthenticationFailureHandler:

public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {

private ObjectMapper objectMapper = new ObjectMapper();

@Override
public void onAuthenticationFailure(
        HttpServletRequest request,
        HttpServletResponse response,
        AuthenticationException exception)
        throws IOException {

    response.setStatus(HttpStatus.UNAUTHORIZED.value());
    response.setContentType("application/json");

    Map<String, Object> error = new HashMap<>();
    error.put("domain", "global");
    error.put("reason", "required");
    error.put("message", "Invalid credentials.");
    ArrayList<Map<String, Object>> errorsList = new ArrayList<>();
    errorsList.add(error);
    Map<String, Object> errors = new HashMap<>();
    errors.put("errors", errorsList);
    errors.put("code", 401);
    errors.put("message", "Invalid credentials.");

    Map<String, Object> data = new HashMap<>();
    data.put(
            "error", errors);

    response.getOutputStream()
            .println(objectMapper.writeValueAsString(data));
    }
}

在身份验证流程中,当我抛出 CredentialsExpiredException、BadCredentialsException 或任何身份验证异常时,将从 AbstractAuthenticationProcessingFilter 调用 unsuccessfulAuthentication 方法,并将执行给定的 AuthenticationFailureHandler:

public final class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final String BEARER = "Bearer";

public TokenAuthenticationFilter(final RequestMatcher requiresAuth) {
    super(requiresAuth);
}

@Override
public Authentication attemptAuthentication(
        final HttpServletRequest request,
        final HttpServletResponse response) {
    final String param = ofNullable(request.getHeader(AUTHORIZATION))
            .orElse(request.getParameter("t"));

    final String token = ofNullable(param)
            .map(value -> removeStart(value, BEARER))
            .map(String::trim)
            .orElseThrow(() -> new BadCredentialsException("Missing Authentication Token"));

    final Authentication auth = new UsernamePasswordAuthenticationToken(null, token);
    return getAuthenticationManager().authenticate(auth);
}

@Override
protected void successfulAuthentication(
        final HttpServletRequest request,
        final HttpServletResponse response,
        final FilterChain chain,
        final Authentication authResult) throws IOException, ServletException {
    super.successfulAuthentication(request, response, chain, authResult);
    chain.doFilter(request, response);
}

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request,
                                          HttpServletResponse response,
                                          AuthenticationException failed)
        throws IOException, ServletException {
    getFailureHandler().onAuthenticationFailure(request, response, failed);
    }
}

关于java - Spring Boot 中永远不会调用 AuthenticationEntryPoint,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54222106/

相关文章:

java - 损坏发生后会发生去优化吗?

spring - 如何通过 Spring Boot 管理控制台更改属性值?

python - 如何判断 "except urllib2.URLError, e"中是否有 e

java - 为什么我在这里得到 java.lang.StackOverflowError?

c# - 检查 null 线程安全吗?

java - JTextPane 中文本颜色更改不准确

java - Java Process.exitValue() 中的值是什么意思?

java - 对可能包含数字的字符串进行排序

java - 为什么我的 Thymeleaf 仅显示列表的最后一行

java - Spring Boot Basic Authentication without Session(Stateless Session)