From 505e32d17d09c120f6f4835247ed924daadf237c Mon Sep 17 00:00:00 2001 From: FatttSnake Date: Fri, 2 Jun 2023 01:31:13 +0800 Subject: [PATCH] Optimized exception interceptor --- .../cfive/pinnacle/config/FilterConfig.java | 18 +++++++++++ .../cfive/pinnacle/config/SecurityConfig.java | 2 +- .../controller/ExceptionController.java | 14 +++++++++ .../exception/TokenHasExpiredException.java | 23 ++++++++++++++ .../pinnacle/filter/ExceptionFilter.java | 30 +++++++++++++++++++ .../filter/JwtAuthenticationTokenFilter.java | 18 ++++------- ...CustomAuthenticationEntryPointHandler.java | 18 +++-------- .../handler/CustomExceptionHandler.java | 15 +++++++++- .../com/cfive/pinnacle/utils/JwtUtil.java | 2 +- .../com/cfive/pinnacle/utils/WebUtil.java | 22 -------------- 10 files changed, 110 insertions(+), 52 deletions(-) create mode 100644 Pinnacle/src/main/java/com/cfive/pinnacle/config/FilterConfig.java create mode 100644 Pinnacle/src/main/java/com/cfive/pinnacle/controller/ExceptionController.java create mode 100644 Pinnacle/src/main/java/com/cfive/pinnacle/exception/TokenHasExpiredException.java create mode 100644 Pinnacle/src/main/java/com/cfive/pinnacle/filter/ExceptionFilter.java diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/config/FilterConfig.java b/Pinnacle/src/main/java/com/cfive/pinnacle/config/FilterConfig.java new file mode 100644 index 0000000..a918bb9 --- /dev/null +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/config/FilterConfig.java @@ -0,0 +1,18 @@ +package com.cfive.pinnacle.config; + +import com.cfive.pinnacle.filter.ExceptionFilter; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FilterConfig { + + @Bean + public FilterRegistrationBean exceptionFilterFilterRegistrationBean(ExceptionFilter exceptionFilter) { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(exceptionFilter); + registrationBean.setName("exceptionFilter"); + registrationBean.setOrder(-100); + return registrationBean; + } +} diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/config/SecurityConfig.java b/Pinnacle/src/main/java/com/cfive/pinnacle/config/SecurityConfig.java index cd36b5e..2af5456 100644 --- a/Pinnacle/src/main/java/com/cfive/pinnacle/config/SecurityConfig.java +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/config/SecurityConfig.java @@ -83,7 +83,7 @@ public class SecurityConfig { // Allow anonymous access .authorizeHttpRequests() - .requestMatchers("/login", "/doc.html", "/swagger-ui/**", "/webjars/**", "/v3/**", "/swagger-ui.html") + .requestMatchers("/login", "/error/thrown", "/doc.html", "/swagger-ui/**", "/webjars/**", "/v3/**", "/swagger-ui.html") .anonymous() // Authentication required diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/controller/ExceptionController.java b/Pinnacle/src/main/java/com/cfive/pinnacle/controller/ExceptionController.java new file mode 100644 index 0000000..c28eefa --- /dev/null +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/controller/ExceptionController.java @@ -0,0 +1,14 @@ +package com.cfive.pinnacle.controller; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/error") +public class ExceptionController { + @RequestMapping("/thrown") + public void thrown(HttpServletRequest request) { + throw (RuntimeException) request.getAttribute("filter.error"); + } +} diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/exception/TokenHasExpiredException.java b/Pinnacle/src/main/java/com/cfive/pinnacle/exception/TokenHasExpiredException.java new file mode 100644 index 0000000..b0f31a4 --- /dev/null +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/exception/TokenHasExpiredException.java @@ -0,0 +1,23 @@ +package com.cfive.pinnacle.exception; + +public class TokenHasExpiredException extends RuntimeException { + public TokenHasExpiredException() { + super("Token has expired"); + } + + public TokenHasExpiredException(String message) { + super(message); + } + + public TokenHasExpiredException(String message, Throwable cause) { + super(message, cause); + } + + public TokenHasExpiredException(Throwable cause) { + super(cause); + } + + public TokenHasExpiredException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/filter/ExceptionFilter.java b/Pinnacle/src/main/java/com/cfive/pinnacle/filter/ExceptionFilter.java new file mode 100644 index 0000000..5aec7e5 --- /dev/null +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/filter/ExceptionFilter.java @@ -0,0 +1,30 @@ +package com.cfive.pinnacle.filter; + +import jakarta.servlet.*; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class ExceptionFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + Filter.super.init(filterConfig); + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + try { + filterChain.doFilter(servletRequest, servletResponse); + } catch (Exception e) { + servletRequest.setAttribute("filter.error", e); + servletRequest.getRequestDispatcher("/error/thrown").forward(servletRequest, servletResponse); + } + } + + @Override + public void destroy() { + Filter.super.destroy(); + } +} diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/filter/JwtAuthenticationTokenFilter.java b/Pinnacle/src/main/java/com/cfive/pinnacle/filter/JwtAuthenticationTokenFilter.java index 96489bb..59b918d 100644 --- a/Pinnacle/src/main/java/com/cfive/pinnacle/filter/JwtAuthenticationTokenFilter.java +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/filter/JwtAuthenticationTokenFilter.java @@ -1,10 +1,9 @@ package com.cfive.pinnacle.filter; -import com.cfive.pinnacle.entity.common.ResponseCode; import com.cfive.pinnacle.entity.permission.LoginUser; +import com.cfive.pinnacle.exception.TokenHasExpiredException; import com.cfive.pinnacle.utils.JwtUtil; import com.cfive.pinnacle.utils.RedisCache; -import com.cfive.pinnacle.utils.WebUtil; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Nonnull; import jakarta.servlet.FilterChain; @@ -33,25 +32,18 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull FilterChain filterChain) throws ServletException, IOException { String token = request.getHeader("token"); - if (!StringUtils.hasText(token)) { + + if (!StringUtils.hasText(token) || "/error/thrown".equals(request.getServletPath())) { filterChain.doFilter(request, response); return; } - try { - JwtUtil.parseJWT(token); - } catch (Exception e) { - String objectResponse = WebUtil.objectResponse(ResponseCode.TOKEN_IS_ILLEGAL, "Token is illegal", null); - WebUtil.renderString(response, objectResponse); - return; - } + JwtUtil.parseJWT(token); String redisKey = "login:" + token; LoginUser loginUser = new ObjectMapper().convertValue(redisCache.getCacheObject(redisKey), LoginUser.class); if (Objects.isNull(loginUser)) { - String objectResponse = WebUtil.objectResponse(ResponseCode.TOKEN_HAS_EXPIRED, "Token has expired", null); - WebUtil.renderString(response, objectResponse); - return; + throw new TokenHasExpiredException(); } UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/handler/CustomAuthenticationEntryPointHandler.java b/Pinnacle/src/main/java/com/cfive/pinnacle/handler/CustomAuthenticationEntryPointHandler.java index f9aca5c..88bfea2 100644 --- a/Pinnacle/src/main/java/com/cfive/pinnacle/handler/CustomAuthenticationEntryPointHandler.java +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/handler/CustomAuthenticationEntryPointHandler.java @@ -1,11 +1,8 @@ package com.cfive.pinnacle.handler; -import com.cfive.pinnacle.entity.common.ResponseCode; -import com.cfive.pinnacle.utils.WebUtil; +import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; @@ -15,15 +12,8 @@ import java.io.IOException; @Component public class CustomAuthenticationEntryPointHandler implements AuthenticationEntryPoint { @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { - String objectResponse; - if (authException instanceof BadCredentialsException) { - objectResponse = WebUtil.objectResponse(ResponseCode.LOGIN_USERNAME_PASSWORD_ERROR, authException.getMessage(), null); - } else if (authException instanceof InsufficientAuthenticationException) { - objectResponse = WebUtil.objectResponse(ResponseCode.UNAUTHORIZED, authException.getMessage(), null); - } else { - objectResponse = WebUtil.objectResponse(ResponseCode.UNAUTHORIZED, authException.getClass().toString() + ": " + authException.getMessage(), null); - } - WebUtil.renderString(response, objectResponse); + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + request.setAttribute("filter.error", authException); + request.getRequestDispatcher("/error/thrown").forward(request, response); } } diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/handler/CustomExceptionHandler.java b/Pinnacle/src/main/java/com/cfive/pinnacle/handler/CustomExceptionHandler.java index b088569..fc50043 100644 --- a/Pinnacle/src/main/java/com/cfive/pinnacle/handler/CustomExceptionHandler.java +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/handler/CustomExceptionHandler.java @@ -1,14 +1,18 @@ package com.cfive.pinnacle.handler; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.exceptions.TokenExpiredException; import com.cfive.pinnacle.entity.common.ResponseCode; import com.cfive.pinnacle.entity.common.ResponseResult; import com.cfive.pinnacle.exception.DataValidationFailedException; +import com.cfive.pinnacle.exception.TokenHasExpiredException; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DuplicateKeyException; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -18,11 +22,20 @@ public class CustomExceptionHandler { @ExceptionHandler(value = Exception.class) public ResponseResult exceptionHandler(Exception e) { + if (e instanceof InsufficientAuthenticationException) { + return ResponseResult.build(ResponseCode.UNAUTHORIZED, e.getMessage(), null); + } + if (e instanceof JWTDecodeException) { + return ResponseResult.build(ResponseCode.TOKEN_IS_ILLEGAL, "Token is illegal", null); + } + if (e instanceof TokenHasExpiredException || e instanceof TokenExpiredException) { + return ResponseResult.build(ResponseCode.TOKEN_HAS_EXPIRED, "Token has expired", null); + } if (e instanceof DuplicateKeyException) { return ResponseResult.build(ResponseCode.DATABASE_SAVE_ERROR, "无法添加重复数据", null); } if (e instanceof BadCredentialsException) { - return ResponseResult.build(ResponseCode.LOGOUT_FAILED, e.getMessage(), null); + return ResponseResult.build(ResponseCode.LOGIN_USERNAME_PASSWORD_ERROR, e.getMessage(), null); } if (e instanceof AccessDeniedException) { return ResponseResult.build(ResponseCode.ACCESS_DENIED, e.getMessage(), null); diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/utils/JwtUtil.java b/Pinnacle/src/main/java/com/cfive/pinnacle/utils/JwtUtil.java index 2307474..ea93b85 100644 --- a/Pinnacle/src/main/java/com/cfive/pinnacle/utils/JwtUtil.java +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/utils/JwtUtil.java @@ -109,7 +109,7 @@ public class JwtUtil { // String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg"; // Claims claims = parseJWT(token); - System.out.println(parseJWT("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJxd2UiLCJpc3MiOiJjZml2ZSIsImV4cCI6MTY4MzE5MzkyOSwiaWF0IjoxNjgzMTkwMzI5LCJqdGkiOiIzOWY5YTcxYTllY2E0Mjg1OGVjNGExODU2ZmQwYjk4OCJ9.4YOOILGWxlnmToWTdo4YoCbfXqvzdJF_Ds4zulDWX1o") + System.out.println(parseJWT("ayJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJxd2UiLCJpc3MiOiJjZml2ZSIsImV4cCI6MTY4MzE5MzkyOSwiaWF0IjoxNjgzMTkwMzI5LCJqdGkiOiIzOWY5YTcxYTllY2E0Mjg1OGVjNGExODU2ZmQwYjk4OCJ9.4YOOILGWxlnmToWTdo4YoCbfXqvzdJF_Ds4zulDWX1o") .getClaims()); } } \ No newline at end of file diff --git a/Pinnacle/src/main/java/com/cfive/pinnacle/utils/WebUtil.java b/Pinnacle/src/main/java/com/cfive/pinnacle/utils/WebUtil.java index 22efaeb..c9bd258 100644 --- a/Pinnacle/src/main/java/com/cfive/pinnacle/utils/WebUtil.java +++ b/Pinnacle/src/main/java/com/cfive/pinnacle/utils/WebUtil.java @@ -1,32 +1,10 @@ package com.cfive.pinnacle.utils; -import com.cfive.pinnacle.entity.common.ResponseResult; import com.cfive.pinnacle.entity.permission.LoginUser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import java.io.IOException; - public class WebUtil { - public static String convert2json(Object object) throws JsonProcessingException { - return new ObjectMapper().writeValueAsString(object); - } - - public static String objectResponse(int resultCode, String msg, Object object) throws JsonProcessingException { - ResponseResult result = ResponseResult.build(resultCode, msg, object); - return convert2json(result); - } - - public static void renderString(HttpServletResponse response, String string) throws IOException { - response.setStatus(200); - response.setContentType("application/json"); - response.setCharacterEncoding("utf-8"); - response.getWriter().print(string); - } - public static LoginUser getLoginUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Object principal = authentication.getPrincipal();