AccessDeniedException指用户访问接口或其他资源时,权限不足引起的异常。本文介绍如何在Spring Security下对AccessDeniedException异常进行个性化的处理。
1.自定义AccessDeniedHandler处理类
import com.alibaba.fastjson.JSONObject;
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;
/**
* 注意:未登录状态下无法触发这个Handler
* @author craigezhong
* @Date 2021/6/19 19:41
*/
@Component("customAccessDeniedHandler")
public class CustomAccessDeniedHandler implements AccessDeniedHandler{
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setStatus(HttpStatus.OK.value());
//设置格式以及返回json数据 方便前台使用reponseJSON接取
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json; charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
JSONObject json = new JSONObject();
json.put("40003", "权限不足,请联系管理员");
out.append(json.toString());
}
}
2.自定义AuthenticationEntryPoint处理类
import com.alibaba.fastjson.JSONObject;
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.PrintWriter;
/**
* @author craigezhong
* @Date 2021/6/21 14:27
*/
@Component("customAuthenticationEntryPoint")
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
JSONObject json = new JSONObject();
json.put("40003", "权限不足,请联系管理员");
out.append(json.toString());
}
}
3.在WebSecurityConfigurerAdapter添加异常处理配置
/**
* notice: The AccessDeniedHandler only applies to authenticated users.
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//配置权限校验
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setSecurityMetadataSource(customMetadataSource);
o.setAccessDecisionManager(urlAccessDecisionManager);
return o;
}
})
.antMatchers("/**", "/get/code", "/authentication/require", "/*.html", "/code/*", "/oauth/*", "/team/**").permitAll()
.anyRequest()
.authenticated()
// 基于表单登录
.and().formLogin()
// 请求登录时跳转的页面
.loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
// 登录的请求url
.loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM)
//登录成功或失败以后的处理url
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler)
// 退出登录
.and().logout()
.logoutUrl(SecurityConstants.DEFAULT_LOGINOUT_PROCESSING_URL_FORM)
.logoutSuccessHandler(myLogoutSuccessHandler);
//异常处理
http.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(customAccessDeniedHandler);
//增加前置校验器 验证码、token
http.addFilterBefore(validateCodeFilter, AbstractPreAuthenticatedProcessingFilter.class);
http.addFilterBefore(checkTokenFilter, AbstractPreAuthenticatedProcessingFilter.class);
}
主要是下面这句:http.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(customAccessDeniedHandler);
4.注意事项
ExceptionTranslationFilter catch到AccessDeniedException后,分为两种情况:
- 如果用户处于未登录(anonymous)状态,会先触发AuthenticationEntryPoint,如果没有配置,则会重定向至登录页;
- 如果用户处于登陆(authenticated)状态,会触发AccessDeniedHandler。
具体参考:ExceptionTranslationFilter
所以,最好将AuthenticationEntryPoint和AccessDeniedHandler都配置上。