1. 结论

**
AuthenticationEntryPoint 用来解决匿名用户访问无权限资源时的异常
AccessDeineHandler 用来解决认证过的用户访问无权限资源时的异常

2. AuthenticationEntryPoint

AuthenticationEntryPoint 是 Spring Security Web 一个概念模型接口,顾名思义,他所建模的概念是:“认证入口点”。
它在用户请求处理过程中遇到认证异常时,被 ExceptionTranslationFilter 用于开启特定认证方案 (authentication schema) 的认证流程。
该接口只定义了一个方法 :

  1. void commence(HttpServletRequest request, HttpServletResponse response,
  2. AuthenticationException authException) throws IOException, ServletException;

这里参数 request 是遇到了认证异常 authException 用户请求,response 是将要返回给客户的相应,方法 commence 实现,也就是相应的认证方案逻辑会修改 response 并返回给用户引导用户进入认证流程。
当用户请求了一个受保护的资源,但是用户没有通过认证,那么抛出异常,AuthenticationEntryPoint.Commence(..)就会被调用。这个对应的代码在ExceptionTranslationFilter中,如下,当ExceptionTranslationFilter catch到异常后,就会间接调用AuthenticationEntryPoint。

  1. public class ExceptionTranslationFilter extends GenericFilterBean {
  2. private AuthenticationEntryPoint authenticationEntryPoint;
  3. ......
  4. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  5. HttpServletRequest request = (HttpServletRequest)req;
  6. HttpServletResponse response = (HttpServletResponse)res;
  7. try {
  8. chain.doFilter(request, response);
  9. this.logger.debug("Chain processed normally");
  10. } catch (IOException var9) {
  11. throw var9;
  12. } catch (Exception var10) {
  13. ......
  14. this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
  15. }
  16. }
  17. private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException {
  18. ......
  19. this.sendStartAuthentication(request, response, chain, (AuthenticationException)exception);
  20. ......
  21. }
  22. protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
  23. SecurityContextHolder.getContext().setAuthentication((Authentication)null);
  24. this.requestCache.saveRequest(request, response);
  25. this.logger.debug("Calling Authentication entry point.");
  26. this.authenticationEntryPoint.commence(request, response, reason);
  27. }
  28. ......

匿名用户访问某个接口时

  1. /**
  2. * 认证失败处理类 返回未授权
  3. * 用来解决匿名用户访问无权限资源时的异常
  4. */
  5. @Component
  6. public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
  7. @Override
  8. public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
  9. throws IOException {
  10. response.setCharacterEncoding("utf-8");
  11. response.setContentType("text/javascript;charset=utf-8");
  12. response.getWriter().print(JSONObject.toJSONString(RestMsg.error("没有访问权限!")));
  13. }
  14. }

3. AccessDeniedHandler

AccessDeniedHandler 仅适用于已通过身份验证的用户。未经身份验证的用户的默认行为是重定向到登录页面(或适用于正在使用的身份验证机制的任何内容)。

已经授权但是没有访问权限

  1. /**
  2. * 认证失败处理类 返回未授权
  3. * 用来解决认证过的用户访问无权限资源时的异常
  4. */
  5. @Component
  6. public class CustomAccessDeniedHandler implements AccessDeniedHandler {
  7. @Override
  8. public void handle(HttpServletRequest request, HttpServletResponse response,
  9. AccessDeniedException accessDeniedException) throws IOException, ServletException {
  10. response.setCharacterEncoding("utf-8");
  11. response.setContentType("text/javascript;charset=utf-8");
  12. response.getWriter().print(JSONObject.toJSONString(RestMsg.error("没有访问权限!")));
  13. }
  14. }

4. 配置

  1. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  2. @Autowired
  3. private CustomAuthenticationEntryPoint authenticationEntryPoint;
  4. @Autowired
  5. private CustomAccessDeniedHandler customAccessDeniedHandler;
  6. // 省略部分代码
  7. @Override
  8. protected void configure(HttpSecurity httpSecurity) throws Exception {
  9. httpSecurity
  10. // 认证失败处理类
  11. .exceptionHandling()
  12. .authenticationEntryPoint(authenticationEntryPoint)
  13. .accessDeniedHandler(customAccessDeniedHandler);
  14. }
  15. // 省略部分代码
  16. }