错误处理

前面介绍了Spring Security中基本的认证和鉴权用法,但我们之前的代码如果出现未认证或未授权的访问时,服务端返回的是Spring Security的默认信息,实际开发中我们通常需要自定义这些错误信息。之前章节中提到过Spring Security中错误处理由ExceptionTranslationFilter过滤器负责,我们可以扩展该过滤器实现自定义错误处理。

扩展AuthenticationEntryPoint和AccessDeniedHandler

AuthenticationEntryPointExceptionTranslationFilter的扩展点,用于处理未认证情况。

AccessDeniedHandlerExceptionTranslationFilter的扩展点,用于处理未授权访问。

我们直接实现这两个接口并注册到Spring容器中,然后在HttpSecurity对其进行配置,即可实现自定义错误处理。

AuthenticationEntryPointImpl.java

package com.gacfox.demo.demosecurity.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gacfox.demo.demosecurity.model.ApiResult;
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.PrintWriter;

@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        ApiResult<?> apiResult = ApiResult.failure("认证失败,请先登录");
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(apiResult);
        PrintWriter printWriter = response.getWriter();
        printWriter.write(json);
        printWriter.close();
    }
}

AccessDeniedHandlerImpl.java

package com.gacfox.demo.demosecurity.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gacfox.demo.demosecurity.model.ApiResult;
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.PrintWriter;

@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        ApiResult<?> apiResult = ApiResult.failure("未授权访问");
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(apiResult);
        PrintWriter printWriter = response.getWriter();
        printWriter.write(json);
        printWriter.close();
    }
}

这里注意这2个扩展点仅支持Servlet原生的HttpServletRequestHttpServletResponse因此非常难用,我们这里只能手动序列化JSON字符串并设置响应头,然后将字符串写入输出流。

HttpSecurity的配置中我们添加如下配置注册我们自定义的扩展点即可。

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

此时如果系统发生未认证或未授权访问时,具体的处理逻辑就会替换为我们自定义的方式了。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap