Browse Source

统一网关错误码响应

tags/v3.1.0
RuoYi 3 years ago
parent
commit
7a35c474d6
  1. 2
      ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java
  2. 10
      ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SecurityUtils.java
  3. 66
      ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java
  4. 11
      ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java
  5. 10
      ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
  6. 79
      ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java
  7. 10
      ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/BlackListUrlFilter.java
  8. 10
      ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java
  9. 14
      ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java
  10. 10
      ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java
  11. 18
      ruoyi-ui/src/utils/ruoyi.js

2
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java

@ -10,7 +10,7 @@ public class CacheConstants
/** /**
* 令牌自定义标识 * 令牌自定义标识
*/ */
public static final String HEADER = "Authorization"; public static final String TOKEN_AUTHENTICATION = "Authorization";
/** /**
* 令牌前缀 * 令牌前缀

10
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SecurityUtils.java

@ -42,7 +42,15 @@ public class SecurityUtils
*/ */
public static String getToken(HttpServletRequest request) public static String getToken(HttpServletRequest request)
{ {
String token = ServletUtils.getRequest().getHeader(CacheConstants.HEADER); String token = request.getHeader(CacheConstants.TOKEN_AUTHENTICATION);
return replaceTokenPrefix(token);
}
/**
* 替换token前缀
*/
public static String replaceTokenPrefix(String token)
{
if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX)) if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX))
{ {
token = token.replace(CacheConstants.TOKEN_PREFIX, ""); token = token.replace(CacheConstants.TOKEN_PREFIX, "");

66
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java

@ -10,11 +10,19 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.constant.Constants; import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
import reactor.core.publisher.Mono;
/** /**
* 客户端工具类 * 客户端工具类
@ -213,4 +221,62 @@ public class ServletUtils
return ""; return "";
} }
} }
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value)
{
return webFluxResponseWriter(response, HttpStatus.OK, value, R.FAIL);
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param code 响应状态码
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code)
{
return webFluxResponseWriter(response, HttpStatus.OK, value, code);
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param status http状态码
* @param code 响应状态码
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code)
{
return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param contentType content-type
* @param status http状态码
* @param code 响应状态码
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code)
{
response.setStatusCode(status);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
R<?> result = R.fail(code, value.toString());
DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes());
return response.writeWith(Mono.just(dataBuffer));
}
} }

11
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java

@ -75,6 +75,17 @@ public class RedisService
} }
/** /**
* 判断 key是否存在
*
* @param key
* @return true 存在 false不存在
*/
public Boolean hasKey(String key)
{
return redisTemplate.hasKey(key);
}
/**
* 获得缓存的基本对象 * 获得缓存的基本对象
* *
* @param key 缓存键值 * @param key 缓存键值

10
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java

@ -73,6 +73,16 @@ public class TokenService
{ {
// 获取请求携带的令牌 // 获取请求携带的令牌
String token = SecurityUtils.getToken(request); String token = SecurityUtils.getToken(request);
return getLoginUser(token);
}
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(String token)
{
if (StringUtils.isNotEmpty(token)) if (StringUtils.isNotEmpty(token))
{ {
String userKey = getTokenKey(token); String userKey = getTokenKey(token);

79
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java

@ -7,19 +7,15 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.constant.CacheConstants; import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.Constants; import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.utils.SecurityUtils;
import com.ruoyi.common.core.utils.ServletUtils; import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.redis.service.RedisService; import com.ruoyi.common.redis.service.RedisService;
@ -35,7 +31,7 @@ import reactor.core.publisher.Mono;
public class AuthFilter implements GlobalFilter, Ordered public class AuthFilter implements GlobalFilter, Ordered
{ {
private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60; private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60;
// 排除过滤的 uri 地址,nacos自行添加 // 排除过滤的 uri 地址,nacos自行添加
@ -44,61 +40,68 @@ public class AuthFilter implements GlobalFilter, Ordered
@Resource(name = "stringRedisTemplate") @Resource(name = "stringRedisTemplate")
private ValueOperations<String, String> sops; private ValueOperations<String, String> sops;
@Autowired @Autowired
private RedisService redisService; private RedisService redisService;
@Override @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{ {
String url = exchange.getRequest().getURI().getPath(); ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder mutate = request.mutate();
String url = request.getURI().getPath();
// 跳过不需要验证的路径 // 跳过不需要验证的路径
if (StringUtils.matches(url, ignoreWhite.getWhites())) if (StringUtils.matches(url, ignoreWhite.getWhites()))
{ {
return chain.filter(exchange); return chain.filter(exchange);
} }
String token = getToken(exchange.getRequest()); String token = getToken(request);
if (StringUtils.isBlank(token)) if (StringUtils.isEmpty(token))
{ {
return setUnauthorizedResponse(exchange, "令牌不能为空"); return unauthorizedResponse(exchange, "令牌不能为空");
} }
String userStr = sops.get(getTokenKey(token)); String userStr = sops.get(getTokenKey(token));
if (StringUtils.isNull(userStr)) if (StringUtils.isEmpty(userStr))
{ {
return setUnauthorizedResponse(exchange, "登录状态已过期"); return unauthorizedResponse(exchange, "登录状态已过期");
} }
JSONObject obj = JSONObject.parseObject(userStr); JSONObject cacheObj = JSONObject.parseObject(userStr);
String userid = obj.getString("userid"); String userid = cacheObj.getString("userid");
String username = obj.getString("username"); String username = cacheObj.getString("username");
if (StringUtils.isBlank(userid) || StringUtils.isBlank(username)) if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))
{ {
return setUnauthorizedResponse(exchange, "令牌验证失败"); return unauthorizedResponse(exchange, "令牌验证失败");
} }
// 设置过期时间 // 设置过期时间
redisService.expire(getTokenKey(token), EXPIRE_TIME); redisService.expire(getTokenKey(token), EXPIRE_TIME);
// 设置用户信息到请求 // 设置用户信息到请求
ServerHttpRequest mutableReq = exchange.getRequest().mutate().header(CacheConstants.DETAILS_USER_ID, userid) addHeader(mutate, CacheConstants.DETAILS_USER_ID, userid);
.header(CacheConstants.DETAILS_USERNAME, ServletUtils.urlEncode(username)).build(); addHeader(mutate, CacheConstants.DETAILS_USERNAME, username);
ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build(); return chain.filter(exchange.mutate().request(mutate.build()).build());
return chain.filter(mutableExchange);
} }
private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String msg) private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value)
{ {
ServerHttpResponse response = exchange.getResponse(); if (value == null)
response.getHeaders().setContentType(MediaType.APPLICATION_JSON); {
response.setStatusCode(HttpStatus.OK); return;
}
String valueStr = value.toString();
String valueEncode = ServletUtils.urlEncode(valueStr);
mutate.header(name, valueEncode);
}
private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg)
{
log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
return bufferFactory.wrap(JSON.toJSONBytes(R.fail(HttpStatus.UNAUTHORIZED.value(), msg)));
}));
} }
/**
* 获取缓存key
*/
private String getTokenKey(String token) private String getTokenKey(String token)
{ {
return CacheConstants.LOGIN_TOKEN_KEY + token; return CacheConstants.LOGIN_TOKEN_KEY + token;
@ -109,12 +112,8 @@ public class AuthFilter implements GlobalFilter, Ordered
*/ */
private String getToken(ServerHttpRequest request) private String getToken(ServerHttpRequest request)
{ {
String token = request.getHeaders().getFirst(CacheConstants.HEADER); String token = request.getHeaders().getFirst(CacheConstants.TOKEN_AUTHENTICATION);
if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX)) return SecurityUtils.replaceTokenPrefix(token);
{
token = token.replace(CacheConstants.TOKEN_PREFIX, "");
}
return token;
} }
@Override @Override

10
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/BlackListUrlFilter.java

@ -5,11 +5,8 @@ import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON; import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import reactor.core.publisher.Mono;
/** /**
* 黑名单过滤器 * 黑名单过滤器
@ -27,10 +24,7 @@ public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUr
String url = exchange.getRequest().getURI().getPath(); String url = exchange.getRequest().getURI().getPath();
if (config.matchBlacklist(url)) if (config.matchBlacklist(url))
{ {
ServerHttpResponse response = exchange.getResponse(); return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问");
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return exchange.getResponse().writeWith(
Mono.just(response.bufferFactory().wrap(JSON.toJSONBytes(AjaxResult.error("请求地址不允许访问")))));
} }
return chain.filter(exchange); return chain.filter(exchange);

10
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java

@ -9,16 +9,13 @@ import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFac
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.gateway.config.properties.CaptchaProperties; import com.ruoyi.gateway.config.properties.CaptchaProperties;
import com.ruoyi.gateway.service.ValidateCodeService; import com.ruoyi.gateway.service.ValidateCodeService;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/** /**
* 验证码过滤器 * 验证码过滤器
@ -60,10 +57,7 @@ public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object>
} }
catch (Exception e) catch (Exception e)
{ {
ServerHttpResponse response = exchange.getResponse(); return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage());
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return exchange.getResponse().writeWith(
Mono.just(response.bufferFactory().wrap(JSON.toJSONBytes(AjaxResult.error(e.getMessage())))));
} }
return chain.filter(exchange); return chain.filter(exchange);
}; };

14
ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java

@ -6,14 +6,10 @@ import org.slf4j.LoggerFactory;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import com.alibaba.fastjson.JSON; import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.domain.R;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
@ -55,12 +51,6 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler
log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage());
response.getHeaders().setContentType(MediaType.APPLICATION_JSON); return ServletUtils.webFluxResponseWriter(response, msg);
response.setStatusCode(HttpStatus.OK);
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
return bufferFactory.wrap(JSON.toJSONBytes(R.fail(msg)));
}));
} }
} }

10
ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java

@ -1,10 +1,8 @@
package com.ruoyi.gateway.handler; package com.ruoyi.gateway.handler;
import java.nio.charset.StandardCharsets;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.core.io.buffer.DataBuffer; import com.ruoyi.common.core.utils.ServletUtils;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler; import org.springframework.web.server.WebExceptionHandler;
@ -19,11 +17,7 @@ public class SentinelFallbackHandler implements WebExceptionHandler
{ {
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
{ {
ServerHttpResponse serverHttpResponse = exchange.getResponse(); return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍后再试");
serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
byte[] datas = "{\"code\":429,\"msg\":\"请求超过最大数,请稍后再试\"}".getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
return serverHttpResponse.writeWith(Mono.just(buffer));
} }
@Override @Override

18
ruoyi-ui/src/utils/ruoyi.js

@ -185,15 +185,15 @@ export function tansParams(params) {
var part = encodeURIComponent(propName) + "="; var part = encodeURIComponent(propName) + "=";
if (value !== null && typeof(value) !== "undefined") { if (value !== null && typeof(value) !== "undefined") {
if (typeof value === 'object') { if (typeof value === 'object') {
for (const key of Object.keys(value)) { for (const key of Object.keys(value)) {
let params = propName + '[' + key + ']'; let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "="; var subPart = encodeURIComponent(params) + "=";
result += subPart + encodeURIComponent(value[key]) + "&"; result += subPart + encodeURIComponent(value[key]) + "&";
} }
} else { } else {
result += part + encodeURIComponent(value) + "&"; result += part + encodeURIComponent(value) + "&";
} }
} }
} }
return result return result
} }

Loading…
Cancel
Save