| | |
| | | package com.xiaopeng.common.security.handler; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.xiaopeng.common.core.constant.HttpStatus; |
| | | import com.xiaopeng.common.core.exception.DemoModeException; |
| | | import com.xiaopeng.common.core.exception.InnerAuthException; |
| | |
| | | import com.xiaopeng.common.core.exception.user.UserAppletException; |
| | | import com.xiaopeng.common.core.utils.StringUtils; |
| | | import com.xiaopeng.common.core.web.domain.AjaxResult; |
| | | import feign.FeignException; |
| | | import org.apache.commons.lang3.RandomStringUtils; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | import org.springframework.web.multipart.MaxUploadSizeExceededException; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.util.UUID; |
| | | |
| | | /** |
| | | * 全局异常处理器 |
| | |
| | | */ |
| | | @RestControllerAdvice |
| | | public class GlobalExceptionHandler { |
| | | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); |
| | | |
| | | @Value("${spring.servlet.multipart.max-file-size:50MB}") |
| | | private String maxFileSize; |
| | | @Value("${spring.servlet.multipart.max-request-size:100MB}") |
| | | private String maxRequestSize; |
| | | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); |
| | | |
| | | @Autowired |
| | | private MessageSource messageSource; |
| | | /** |
| | | * 权限码异常 |
| | | */ |
| | | @ExceptionHandler(NotPermissionException.class) |
| | | public AjaxResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); |
| | | return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); |
| | | } |
| | | |
| | | /** |
| | | * 角色权限异常 |
| | | */ |
| | | @ExceptionHandler(NotRoleException.class) |
| | | public AjaxResult handleNotRoleException(NotRoleException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); |
| | | return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); |
| | | } |
| | | |
| | | /** |
| | | * 没有登录异常 |
| | | * @param e |
| | | * @param request |
| | | * @return |
| | | */ |
| | | @ExceptionHandler(NotLoginException.class) |
| | | public AjaxResult notLoginException(NotLoginException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',登录校验失败'{}'", requestURI, e.getMessage()); |
| | | return AjaxResult.error(HttpStatus.UNAUTHORIZED, e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 请求方式不支持 |
| | | */ |
| | | @ExceptionHandler(HttpRequestMethodNotSupportedException.class) |
| | | public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, |
| | | HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 业务异常 |
| | | */ |
| | | @ExceptionHandler(ServiceException.class) |
| | | public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) { |
| | | log.error(e.getMessage(), e); |
| | | Integer code = e.getCode(); |
| | | String msg = e.getMessage(); |
| | | if (StringUtils.hasText(e.getMessageKey())) { |
| | | msg = messageSource.getMessage(e.getMessageKey(), e.getArgs(), LocaleContextHolder.getLocale()); |
| | | } |
| | | return StringUtils.isNotNull(code) ? AjaxResult.error(code, msg) : AjaxResult.error(msg); |
| | | } |
| | | |
| | | /** |
| | | * 拦截未知的运行时异常 |
| | | */ |
| | | @ExceptionHandler(RuntimeException.class) |
| | | public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',发生未知异常.", requestURI, e); |
| | | return AjaxResult.error("系统繁忙,请稍后重试"); |
| | | } |
| | | |
| | | /** |
| | | * 系统异常 |
| | | */ |
| | | @ExceptionHandler(Exception.class) |
| | | public AjaxResult handleException(Exception e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',发生系统异常.", requestURI, e); |
| | | return AjaxResult.error("系统繁忙,请稍后重试"); |
| | | } |
| | | |
| | | /** |
| | | * 自定义验证异常 |
| | | */ |
| | | @ExceptionHandler(BindException.class) |
| | | public AjaxResult handleBindException(BindException e) { |
| | | log.error(e.getMessage(), e); |
| | | String message = e.getAllErrors().get(0).getDefaultMessage(); |
| | | @Value("${spring.servlet.multipart.max-file-size:50MB}") |
| | | private String maxFileSize; |
| | | @Value("${spring.servlet.multipart.max-request-size:100MB}") |
| | | private String maxRequestSize; |
| | | |
| | | // 支持国际化消息键(格式:{message.key}) |
| | | if (StringUtils.hasText(message) && message.startsWith("{") && message.endsWith("}")) { |
| | | // 提取消息键 |
| | | String messageKey = message.substring(1, message.length() - 1); |
| | | try { |
| | | // 使用 MessageSource 获取国际化消息 |
| | | message = messageSource.getMessage(messageKey, null, LocaleContextHolder.getLocale()); |
| | | } catch (Exception ex) { |
| | | log.warn("国际化消息键不存在: {}", messageKey); |
| | | // 返回消息键本身作为降级方案 |
| | | message = messageKey; |
| | | } |
| | | } |
| | | @Value("${xiaopeng.xtiger.sso.issuer}") |
| | | private String issuer; |
| | | |
| | | return AjaxResult.error(message); |
| | | } |
| | | |
| | | /** |
| | | * 自定义验证异常 |
| | | */ |
| | | @ExceptionHandler(MethodArgumentNotValidException.class) |
| | | public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { |
| | | log.error(e.getMessage(), e); |
| | | String message = e.getBindingResult().getFieldError().getDefaultMessage(); |
| | | @Value("${xiaopeng.xtiger.sso.appId}") |
| | | private String appId; |
| | | |
| | | // 支持国际化消息键(格式:{message.key}) |
| | | if (StringUtils.hasText(message) && message.startsWith("{") && message.endsWith("}")) { |
| | | // 提取消息键 |
| | | String messageKey = message.substring(1, message.length() - 1); |
| | | try { |
| | | // 使用 MessageUtils 获取国际化消息 |
| | | message = messageSource.getMessage(messageKey, null, LocaleContextHolder.getLocale()); |
| | | } catch (Exception ex) { |
| | | log.warn("国际化消息键不存在: {}", messageKey); |
| | | // 返回消息键本身作为降级方案 |
| | | message = messageKey; |
| | | } |
| | | } |
| | | @Value("${xiaopeng.xtiger.sso.serverHost}") |
| | | private String serverHost; |
| | | |
| | | return AjaxResult.error(message); |
| | | } |
| | | |
| | | /** |
| | | * 内部认证异常 |
| | | */ |
| | | @ExceptionHandler(InnerAuthException.class) |
| | | public AjaxResult handleInnerAuthException(InnerAuthException e) { |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 演示模式异常 |
| | | */ |
| | | @ExceptionHandler(DemoModeException.class) |
| | | public AjaxResult handleDemoModeException(DemoModeException e) { |
| | | return AjaxResult.error("演示模式,不允许操作"); |
| | | } |
| | | |
| | | @ExceptionHandler(MaxUploadSizeExceededException.class) |
| | | public AjaxResult fileUpLoad(MaxUploadSizeExceededException e) { |
| | | log.error("上传文件异常 => : {}", e.getMessage()); |
| | | return AjaxResult.error("文件识别大小超出限制,允许的大小在" + maxFileSize); |
| | | } |
| | | |
| | | /** |
| | | * 学生端单点登录-异常信息 |
| | | */ |
| | | @ExceptionHandler(UserAppletException.class) |
| | | public AjaxResult<String> studyLoginExceptionHandler(UserAppletException e) { |
| | | return AjaxResult.error(e.getCode(), e.getMessage()); |
| | | } |
| | | @Autowired |
| | | private MessageSource messageSource; |
| | | |
| | | /** |
| | | * 权限码异常 |
| | | */ |
| | | @ExceptionHandler(NotPermissionException.class) |
| | | public AjaxResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); |
| | | return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); |
| | | } |
| | | |
| | | /** |
| | | * 角色权限异常 |
| | | */ |
| | | @ExceptionHandler(NotRoleException.class) |
| | | public AjaxResult handleNotRoleException(NotRoleException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); |
| | | return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); |
| | | } |
| | | |
| | | /** |
| | | * 没有登录异常 |
| | | * |
| | | * @param e |
| | | * @param request |
| | | * @return |
| | | */ |
| | | @ExceptionHandler(NotLoginException.class) |
| | | public AjaxResult notLoginException(NotLoginException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',登录校验失败'{}'", requestURI, e.getMessage()); |
| | | String s = UUID.randomUUID().toString(); |
| | | String state1 = s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24); |
| | | String s1 = UUID.randomUUID().toString(); |
| | | String state2 = s1.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24); |
| | | String nonce = RandomStringUtils.randomAlphanumeric(6); |
| | | JSONObject jsonObject = JSON.parseObject("{\n" + |
| | | " \"uid\": null,\n" + |
| | | " \"username\": null,\n" + |
| | | " \"authUrl\": \"" + issuer + "/oidc/auth?scope=openid+profile+offline_access+phone+email&response_type=code&redirect_uri=" + serverHost + "/dashboard&state=" + state1 + "&nonce=" + nonce + "&client_id=" + appId + "\",\n" + |
| | | " \"logoutUrl\": null,\n" + |
| | | " \"callbackUrl\": \"" + serverHost + "/dashboard?state=" + state2 + "\"\n" + |
| | | " }"); |
| | | return AjaxResult.error(HttpStatus.UNAUTHORIZED, e.getMessage(), jsonObject); |
| | | } |
| | | |
| | | /** |
| | | * 请求方式不支持 |
| | | */ |
| | | @ExceptionHandler(HttpRequestMethodNotSupportedException.class) |
| | | public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, |
| | | HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 业务异常 |
| | | */ |
| | | @ExceptionHandler(ServiceException.class) |
| | | public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) { |
| | | log.error(e.getMessage(), e); |
| | | Integer code = e.getCode(); |
| | | String msg = e.getMessage(); |
| | | if (StringUtils.hasText(e.getMessageKey())) { |
| | | msg = messageSource.getMessage(e.getMessageKey(), e.getArgs(), LocaleContextHolder.getLocale()); |
| | | } |
| | | return StringUtils.isNotNull(code) ? AjaxResult.error(code, msg) : AjaxResult.error(msg); |
| | | } |
| | | |
| | | /** |
| | | * 拦截未知的运行时异常 |
| | | */ |
| | | @ExceptionHandler(RuntimeException.class) |
| | | public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',发生未知异常.", requestURI, e); |
| | | return AjaxResult.error("系统繁忙,请稍后重试"); |
| | | } |
| | | |
| | | /** |
| | | * 系统异常 |
| | | */ |
| | | @ExceptionHandler(Exception.class) |
| | | public AjaxResult handleException(Exception e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',发生系统异常.", requestURI, e); |
| | | return AjaxResult.error("系统繁忙,请稍后重试"); |
| | | } |
| | | |
| | | /** |
| | | * 自定义验证异常 |
| | | */ |
| | | @ExceptionHandler(BindException.class) |
| | | public AjaxResult handleBindException(BindException e) { |
| | | log.error(e.getMessage(), e); |
| | | String message = e.getAllErrors().get(0).getDefaultMessage(); |
| | | |
| | | // 支持国际化消息键(格式:{message.key}) |
| | | if (StringUtils.hasText(message) && message.startsWith("{") && message.endsWith("}")) { |
| | | // 提取消息键 |
| | | String messageKey = message.substring(1, message.length() - 1); |
| | | try { |
| | | // 使用 MessageSource 获取国际化消息 |
| | | message = messageSource.getMessage(messageKey, null, LocaleContextHolder.getLocale()); |
| | | } catch (Exception ex) { |
| | | log.warn("国际化消息键不存在: {}", messageKey); |
| | | // 返回消息键本身作为降级方案 |
| | | message = messageKey; |
| | | } |
| | | } |
| | | |
| | | return AjaxResult.error(message); |
| | | } |
| | | |
| | | /** |
| | | * 自定义验证异常 |
| | | */ |
| | | @ExceptionHandler(MethodArgumentNotValidException.class) |
| | | public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { |
| | | log.error(e.getMessage(), e); |
| | | // 收集所有字段校验错误,格式:fieldName: message |
| | | StringBuilder sb = new StringBuilder(); |
| | | e.getBindingResult().getFieldErrors().forEach(fieldError -> { |
| | | String message = fieldError.getDefaultMessage(); |
| | | if (StringUtils.hasText(message) && message.startsWith("{") && message.endsWith("}")) { |
| | | String messageKey = message.substring(1, message.length() - 1); |
| | | try { |
| | | message = messageSource.getMessage(messageKey, null, LocaleContextHolder.getLocale()); |
| | | } catch (Exception ex) { |
| | | log.warn("国际化消息键不存在: {}", messageKey); |
| | | message = messageKey; |
| | | } |
| | | } |
| | | if (sb.length() > 0) { |
| | | sb.append("; "); |
| | | } |
| | | sb.append(message); |
| | | // sb.append(fieldError.getField()).append(": ").append(message); |
| | | }); |
| | | return AjaxResult.error(sb.toString()); |
| | | } |
| | | |
| | | /** |
| | | * 内部认证异常 |
| | | */ |
| | | @ExceptionHandler(InnerAuthException.class) |
| | | public AjaxResult handleInnerAuthException(InnerAuthException e) { |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 演示模式异常 |
| | | */ |
| | | @ExceptionHandler(DemoModeException.class) |
| | | public AjaxResult handleDemoModeException(DemoModeException e) { |
| | | return AjaxResult.error("演示模式,不允许操作"); |
| | | } |
| | | |
| | | /** |
| | | * Feign调用异常 - 专门处理403 Forbidden异常 |
| | | * 解析并返回原始响应中的code、msg、data |
| | | */ |
| | | @ExceptionHandler(FeignException.class) |
| | | public AjaxResult handleFeignException(FeignException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}', Feign调用403异常: {}", requestURI, e.getMessage()); |
| | | |
| | | // 获取原始响应内容 |
| | | String responseBody = e.contentUTF8(); |
| | | if (StringUtils.hasText(responseBody)) { |
| | | try { |
| | | // 使用 Jackson 解析 JSON 响应 |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | JsonNode jsonNode = objectMapper.readTree(responseBody); |
| | | |
| | | // 提取 code、msg、data 字段 |
| | | Integer code = jsonNode.has("code") ? jsonNode.get("code").asInt() : null; |
| | | String msg = jsonNode.has("msg") ? jsonNode.get("msg").asText() : null; |
| | | Object data = jsonNode.has("data") && !jsonNode.get("data").isNull() ? |
| | | objectMapper.treeToValue(jsonNode.get("data"), Object.class) : null; |
| | | |
| | | // 直接使用 AjaxResult 构造方法返回原始的code、msg、data |
| | | if (code != null) { |
| | | return new AjaxResult(code, msg, data); |
| | | } else { |
| | | return AjaxResult.error(msg != null ? msg : "服务调用失败"); |
| | | } |
| | | } catch (Exception ex) { |
| | | log.warn("解析Feign异常响应内容失败,返回默认错误: {}", ex.getMessage()); |
| | | // 解析失败时返回默认的403错误 |
| | | return AjaxResult.error(HttpStatus.FORBIDDEN, "服务调用失败"); |
| | | } |
| | | } |
| | | |
| | | // 如果没有响应体,返回默认的403错误 |
| | | return AjaxResult.error(HttpStatus.FORBIDDEN, "服务调用失败"); |
| | | } |
| | | |
| | | @ExceptionHandler(MaxUploadSizeExceededException.class) |
| | | public AjaxResult fileUpLoad(MaxUploadSizeExceededException e) { |
| | | log.error("上传文件异常 => : {}", e.getMessage()); |
| | | return AjaxResult.error("文件识别大小超出限制,允许的大小在" + maxFileSize); |
| | | } |
| | | |
| | | /** |
| | | * 学生端单点登录-异常信息 |
| | | */ |
| | | @ExceptionHandler(UserAppletException.class) |
| | | public AjaxResult<String> studyLoginExceptionHandler(UserAppletException e) { |
| | | return AjaxResult.error(e.getCode(), e.getMessage()); |
| | | } |
| | | } |