spring Security 异常处理

3 篇文章 1 订阅
1 篇文章 0 订阅

目录

一、异常信息类

二、自定义异常处理配置

三、ExceptionTranslationFilter 原理分析


一、异常信息类

Spring Security 中异常主要分为两大类:

  • AuthenticationException:认证异常
  • AccessDeniedException:授权异常

        其中认证所涉及异常类型比较多,默认提供的异常类型如下:

        7ab7cf166495bc67e6f700f5e97de8ed.png

        相比于认证异常,权限异常类就要少了很多,默认提供的权限异常如下:

        在实际项目开发中,如果默认提供异常无法满足需求时,就需要根据实际需要来自定义异常类。 

二、自定义异常处理配置

        首先异常处理设计到两个 Handler 进行处理 ,一个是处理认证异常的Handler处理器 AuthenticationEntryPoint,一个是授权异常的Handler处理器 AccessDeniedHandler

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .mvcMatchers("/index").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .exceptionHandling()
                // 认证异常处理handler 使用的 lambda的内部的实现
                .authenticationEntryPoint((request,response,authenticationException)->{
                    Map<String,Object> rs = new HashMap<>();
                    rs.put("code",401);
                    rs.put("msg","尚未认证!");
                    ObjectMapper objectMapper = new ObjectMapper();
                    String json = objectMapper.writeValueAsString(rs);
                    response.setStatus(200);
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    response.getWriter().println(json);

                })
             // 授权异常处理handler 使用的 lambda的内部的实现
                .accessDeniedHandler((request,response,accessDeniedException)->{
                    Map<String,Object> rs = new HashMap<>();
                    rs.put("code",401);
                    rs.put("msg","没有权限!");
                    ObjectMapper objectMapper = new ObjectMapper();
                    String json = objectMapper.writeValueAsString(rs);
                    response.setStatus(200);
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    response.getWriter().println(json);
                })
                .and()
                .csrf().disable();

    }
}

三、ExceptionTranslationFilter 原理分析

  1. ExceptionTranslationFilter 的doFilter 处理器它的作用是监听所有的访问状态。 
    1. 直接方法,监听执行是否出现异

    2. 如果抛出的是 IO 异常信息则直接抛出,不处理。

    3. 通过异常选择器进行的异常的信息的过滤

    4. 尝试去获取认证异常类型的异常对象,如果没有获取成功,则会去尝试获取授权类型的异常对象,如果获取两个异常类型其中之一,则会去执行对该异常的处理,否则则会报出去。  
        
    5. 	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      			throws IOException, ServletException {
      		HttpServletRequest request = (HttpServletRequest) req;
      		HttpServletResponse response = (HttpServletResponse) res;
      
      		// 1.直接放行,监听执行是否出现异常
      		try {
      			chain.doFilter(request, response);
      
      			logger.debug("Chain processed normally");
      		}
      		// 2.放行 IO 异常
      		catch (IOException ex) {
      			throw ex;
      		}
      		// 3.处理异常
      		catch (Exception ex) {
      			// Try to extract a SpringSecurityException from the stacktrace
      			//  4.尝试从stacktrace中提取SpringSecurityException 
      			Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
      			// 5.从异常选择器中获取的第一个 AuthenticationException 类型的 异常
      			RuntimeException ase = (AuthenticationException) throwableAnalyzer
      					.getFirstThrowableOfType(AuthenticationException.class, causeChain);
      			// 判断是否有取出异常值
      			if (ase == null) {
      				// 再次尝试获取授权异常
      				ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(
      						AccessDeniedException.class, causeChain);
      			}
      			// 异常信息不为null
      			if (ase != null) {
      				// 判断是否委托
      				if (response.isCommitted()) {
      					throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", ex);
      				}
      				// 异常的处理
      				handleSpringSecurityException(request, response, chain, ase);
      			}
      			else {
      				// Rethrow ServletExceptions and RuntimeExceptions as-is
      				if (ex instanceof ServletException) {
      					throw (ServletException) ex;
      				}
      				else if (ex instanceof RuntimeException) {
      					throw (RuntimeException) ex;
      				}
      
      				// Wrap other Exceptions. This shouldn't actually happen
      				// as we've already covered all the possibilities for doFilter
      				throw new RuntimeException(ex);
      			}
      		}
      	}

  2. 真正出处理异常的方法 handleSpringSecurityException       

                

  •  1. 首先判断是否是认证相关异常,如果是认证相关异常,则会调用 sendStartAuthentication 方法,将认证信息移除,并调用 AuthenticationEntryPoint commence 进行结果的处理
  • 2. 如果不是认证相关异常,则会获取当前认证信息,根据认证信息的类型去判断是否 匿名用户 或者是 记住我的用户 类型 如果是则会调用 sendStartAuthentication 的 认证异常处理器进行处理,否则则会调用 accessDeniedHandler.handle 的授权异常处理器进行处理。
	private void handleSpringSecurityException(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain, RuntimeException exception)
			throws IOException, ServletException {
		
		// 判断异常是否 认证异常
		if (exception instanceof AuthenticationException) {
			logger.debug(
					"Authentication exception occurred; redirecting to authentication entry point",
					exception);
			// 开始发出异常信息给响应者
			sendStartAuthentication(request, response, chain,
					(AuthenticationException) exception);
		}

		// 判断异常是否 授权异常类型 
		else if (exception instanceof AccessDeniedException) {
			// 如果是授权异常,那么是有认证信息,则取出认证信息
			Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
			// 判断是否匿名用户,或者是 记住我的用户信息
			if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {
				logger.debug(
						"Access is denied (user is " + (authenticationTrustResolver.isAnonymous(authentication) ? "anonymous" : "not fully authenticated") + "); redirecting to authentication entry point",
						exception);
				// 开始发出异常信息给响应者
				sendStartAuthentication(
						request,
						response,
						chain,
						new InsufficientAuthenticationException(
							messages.getMessage(
								"ExceptionTranslationFilter.insufficientAuthentication",
								"Full authentication is required to access this resource")));
			}
			else {
				// 如果都不是以上的用户信息,则交给 授权处理器
				logger.debug(
						"Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
						exception);
				// 授权处理器去执行
				accessDeniedHandler.handle(request, response,
						(AccessDeniedException) exception);
			}
		}
	}
  • 3.认证异常的处理方法 ExceptionTranslationFilter 的sendStartAuthentication
    • 该方法是处理 认证失败的异常

    • 1. 将当前的用户信息情况
    • 2.将请求和响应对象放入session中
    • 3.调用认证移异常处理器的执行方法。
protected void sendStartAuthentication(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain,
			AuthenticationException reason) throws ServletException, IOException {
		// SEC-112: Clear the SecurityContextHolder's Authentication, as the
		// existing Authentication is no longer considered valid
		SecurityContextHolder.getContext().setAuthentication(null);
		requestCache.saveRequest(request, response);
		logger.debug("Calling Authentication entry point.");
		authenticationEntryPoint.commence(request, response, reason);
	}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security提供了许多方式来处理身份验证和授权的异常。下面是一些常见的异常处理方式: 1. 使用Spring的全局异常处理器:可以通过在应用程序中定义一个`@ControllerAdvice`类,并使用`@ExceptionHandler`注解来处理Spring Security的异常。在异常处理方法中,可以根据不同的异常类型进行相应的处理,例如返回自定义的错误页面或者JSON响应。 ```java @ControllerAdvice public class SecurityExceptionHandler { @ExceptionHandler(AuthenticationException.class) public ResponseEntity<String> handleAuthenticationException(AuthenticationException ex) { // 处理身份验证异常 return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ex.getMessage()); } @ExceptionHandler(AccessDeniedException.class) public ResponseEntity<String> handleAccessDeniedException(AccessDeniedException ex) { // 处理访问拒绝异常 return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ex.getMessage()); } // 其他异常处理方法... } ``` 2. 自定义AccessDeniedHandler:可以实现`AccessDeniedHandler`接口来处理访问拒绝异常。可以在`handle()`方法中自定义处理逻辑,例如返回自定义的错误页面或者JSON响应。 ```java public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException ex) throws IOException, ServletException { // 处理访问拒绝异常 response.sendError(HttpServletResponse.SC_FORBIDDEN, ex.getMessage()); } } ``` 然后,在Spring Security配置中指定自定义的`AccessDeniedHandler`: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { // ... @Override protected void configure(HttpSecurity http) throws Exception { http // ... .exceptionHandling() .accessDeniedHandler(new CustomAccessDeniedHandler()) // ... } // ... } ``` 这些只是处理Spring Security异常的两种常见方式,你可以根据实际需求选择适合的方式进行异常处理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值