1
0
mirror of https://github.com/FatttSnake/Pinnacle-OA.git synced 2026-04-05 23:11:24 +08:00

Optimized exception interceptor

This commit is contained in:
2023-06-02 01:31:13 +08:00
parent 17aa75fd66
commit 505e32d17d
10 changed files with 110 additions and 52 deletions

View File

@@ -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<ExceptionFilter> exceptionFilterFilterRegistrationBean(ExceptionFilter exceptionFilter) {
FilterRegistrationBean<ExceptionFilter> registrationBean = new FilterRegistrationBean<>(exceptionFilter);
registrationBean.setName("exceptionFilter");
registrationBean.setOrder(-100);
return registrationBean;
}
}

View File

@@ -83,7 +83,7 @@ public class SecurityConfig {
// Allow anonymous access // Allow anonymous access
.authorizeHttpRequests() .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() .anonymous()
// Authentication required // Authentication required

View File

@@ -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");
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -1,10 +1,9 @@
package com.cfive.pinnacle.filter; package com.cfive.pinnacle.filter;
import com.cfive.pinnacle.entity.common.ResponseCode;
import com.cfive.pinnacle.entity.permission.LoginUser; import com.cfive.pinnacle.entity.permission.LoginUser;
import com.cfive.pinnacle.exception.TokenHasExpiredException;
import com.cfive.pinnacle.utils.JwtUtil; import com.cfive.pinnacle.utils.JwtUtil;
import com.cfive.pinnacle.utils.RedisCache; import com.cfive.pinnacle.utils.RedisCache;
import com.cfive.pinnacle.utils.WebUtil;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Nonnull; import jakarta.annotation.Nonnull;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
@@ -33,25 +32,18 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Override @Override
protected void doFilterInternal(HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull FilterChain filterChain) throws ServletException, IOException { protected void doFilterInternal(HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("token"); String token = request.getHeader("token");
if (!StringUtils.hasText(token)) {
if (!StringUtils.hasText(token) || "/error/thrown".equals(request.getServletPath())) {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
return; return;
} }
try { JwtUtil.parseJWT(token);
JwtUtil.parseJWT(token);
} catch (Exception e) {
String objectResponse = WebUtil.objectResponse(ResponseCode.TOKEN_IS_ILLEGAL, "Token is illegal", null);
WebUtil.renderString(response, objectResponse);
return;
}
String redisKey = "login:" + token; String redisKey = "login:" + token;
LoginUser loginUser = new ObjectMapper().convertValue(redisCache.getCacheObject(redisKey), LoginUser.class); LoginUser loginUser = new ObjectMapper().convertValue(redisCache.getCacheObject(redisKey), LoginUser.class);
if (Objects.isNull(loginUser)) { if (Objects.isNull(loginUser)) {
String objectResponse = WebUtil.objectResponse(ResponseCode.TOKEN_HAS_EXPIRED, "Token has expired", null); throw new TokenHasExpiredException();
WebUtil.renderString(response, objectResponse);
return;
} }
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());

View File

@@ -1,11 +1,8 @@
package com.cfive.pinnacle.handler; package com.cfive.pinnacle.handler;
import com.cfive.pinnacle.entity.common.ResponseCode; import jakarta.servlet.ServletException;
import com.cfive.pinnacle.utils.WebUtil;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; 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.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -15,15 +12,8 @@ import java.io.IOException;
@Component @Component
public class CustomAuthenticationEntryPointHandler implements AuthenticationEntryPoint { public class CustomAuthenticationEntryPointHandler implements AuthenticationEntryPoint {
@Override @Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
String objectResponse; request.setAttribute("filter.error", authException);
if (authException instanceof BadCredentialsException) { request.getRequestDispatcher("/error/thrown").forward(request, response);
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);
} }
} }

View File

@@ -1,14 +1,18 @@
package com.cfive.pinnacle.handler; 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.ResponseCode;
import com.cfive.pinnacle.entity.common.ResponseResult; import com.cfive.pinnacle.entity.common.ResponseResult;
import com.cfive.pinnacle.exception.DataValidationFailedException; import com.cfive.pinnacle.exception.DataValidationFailedException;
import com.cfive.pinnacle.exception.TokenHasExpiredException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException; 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.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
@@ -18,11 +22,20 @@ public class CustomExceptionHandler {
@ExceptionHandler(value = Exception.class) @ExceptionHandler(value = Exception.class)
public ResponseResult<?> exceptionHandler(Exception e) { 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) { if (e instanceof DuplicateKeyException) {
return ResponseResult.build(ResponseCode.DATABASE_SAVE_ERROR, "无法添加重复数据", null); return ResponseResult.build(ResponseCode.DATABASE_SAVE_ERROR, "无法添加重复数据", null);
} }
if (e instanceof BadCredentialsException) { 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) { if (e instanceof AccessDeniedException) {
return ResponseResult.build(ResponseCode.ACCESS_DENIED, e.getMessage(), null); return ResponseResult.build(ResponseCode.ACCESS_DENIED, e.getMessage(), null);

View File

@@ -109,7 +109,7 @@ public class JwtUtil {
// String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg"; // String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";
// Claims claims = parseJWT(token); // Claims claims = parseJWT(token);
System.out.println(parseJWT("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJxd2UiLCJpc3MiOiJjZml2ZSIsImV4cCI6MTY4MzE5MzkyOSwiaWF0IjoxNjgzMTkwMzI5LCJqdGkiOiIzOWY5YTcxYTllY2E0Mjg1OGVjNGExODU2ZmQwYjk4OCJ9.4YOOILGWxlnmToWTdo4YoCbfXqvzdJF_Ds4zulDWX1o") System.out.println(parseJWT("ayJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJxd2UiLCJpc3MiOiJjZml2ZSIsImV4cCI6MTY4MzE5MzkyOSwiaWF0IjoxNjgzMTkwMzI5LCJqdGkiOiIzOWY5YTcxYTllY2E0Mjg1OGVjNGExODU2ZmQwYjk4OCJ9.4YOOILGWxlnmToWTdo4YoCbfXqvzdJF_Ds4zulDWX1o")
.getClaims()); .getClaims());
} }
} }

View File

@@ -1,32 +1,10 @@
package com.cfive.pinnacle.utils; package com.cfive.pinnacle.utils;
import com.cfive.pinnacle.entity.common.ResponseResult;
import com.cfive.pinnacle.entity.permission.LoginUser; 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.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import java.io.IOException;
public class WebUtil { 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<Object> 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() { public static LoginUser getLoginUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal(); Object principal = authentication.getPrincipal();