package com.ruoyi.gateway.filter;
|
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
import org.springframework.core.Ordered;
|
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpMethod;
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
import org.springframework.stereotype.Component;
|
import org.springframework.web.server.ServerWebExchange;
|
import com.ruoyi.common.core.constant.CacheConstants;
|
import com.ruoyi.common.core.constant.HttpStatus;
|
import com.ruoyi.common.core.constant.SecurityConstants;
|
import com.ruoyi.common.core.constant.TokenConstants;
|
import com.ruoyi.common.core.utils.JwtUtils;
|
import com.ruoyi.common.core.utils.ServletUtils;
|
import com.ruoyi.common.core.utils.StringUtils;
|
import com.ruoyi.common.redis.service.RedisService;
|
import com.ruoyi.gateway.config.properties.IgnoreWhiteProperties;
|
import io.jsonwebtoken.Claims;
|
import reactor.core.publisher.Mono;
|
|
import java.util.HashMap;
|
import java.util.Map;
|
|
|
/**
|
* 网关鉴权
|
*
|
* @author ruoyi
|
*/
|
@Component
|
public class AuthFilter implements GlobalFilter, Ordered {
|
private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
|
|
// 排除过滤的 uri 地址,nacos自行添加
|
@Autowired
|
private IgnoreWhiteProperties ignoreWhite;
|
|
@Autowired
|
private RedisService redisService;
|
|
|
|
@Override
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
ServerHttpRequest request = exchange.getRequest();
|
ServerHttpRequest.Builder mutate = request.mutate();
|
|
String url = request.getURI().getPath();
|
// 跳过不需要验证的路径
|
if (StringUtils.matches(url, ignoreWhite.getWhites())) {
|
return chain.filter(exchange);
|
}
|
//防抖校验
|
try {
|
antiShake(request);
|
}catch (Exception e){
|
log.error("[重复提交]请求路径:{}", exchange.getRequest().getPath());
|
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage(), HttpStatus.SUCCESS);
|
}
|
|
//校验账户是否有效
|
try {
|
verifyToken(request);
|
}catch (Exception e){
|
return unauthorizedResponse(exchange, e.getMessage());
|
}
|
String token = getToken(request);
|
Claims claims = JwtUtils.parseToken(token);
|
String userkey = JwtUtils.getUserKey(claims);
|
String userid = JwtUtils.getUserId(claims);
|
String username = JwtUtils.getUserName(claims);
|
|
// 设置用户信息到请求
|
addHeader(mutate, SecurityConstants.USER_KEY, userkey);
|
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid);
|
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);
|
// 内部请求来源参数清除
|
removeHeader(mutate, SecurityConstants.FROM_SOURCE);
|
return chain.filter(exchange.mutate().request(mutate.build()).build());
|
}
|
|
private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {
|
if (value == null) {
|
return;
|
}
|
String valueStr = value.toString();
|
String valueEncode = ServletUtils.urlEncode(valueStr);
|
mutate.header(name, valueEncode);
|
}
|
|
private void removeHeader(ServerHttpRequest.Builder mutate, String name) {
|
mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
|
}
|
|
private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
|
log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath() + "\n" + msg);
|
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
|
}
|
|
/**
|
* 获取缓存key
|
*/
|
private String getTokenKey(String token) {
|
return CacheConstants.LOGIN_TOKEN_KEY + token;
|
}
|
|
/**
|
* 获取请求token
|
*/
|
private String getToken(ServerHttpRequest request) {
|
String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
|
// 如果前端设置了令牌前缀,则裁剪掉前缀
|
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
|
token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
|
}
|
return token;
|
}
|
|
|
/**
|
* 防抖处理
|
*/
|
public void antiShake(ServerHttpRequest request) throws Exception{
|
HttpMethod method = request.getMethod();
|
if(HttpMethod.OPTIONS == method){
|
return;
|
}
|
HttpHeaders headers = request.getHeaders();
|
String client = headers.getFirst("client");
|
String timestamp = headers.getFirst("timestamp");
|
if(StringUtils.isEmpty(client)){
|
throw new RuntimeException("参数异常");
|
}
|
if(StringUtils.isEmpty(timestamp)){
|
throw new RuntimeException("参数异常");
|
}
|
String url = request.getURI().getPath();
|
Map<String, Object> cacheMap = redisService.getCacheMap(client);
|
if(null == cacheMap){
|
cacheMap = new HashMap<>();
|
cacheMap.put(url, timestamp);
|
redisService.setCacheMap(client, cacheMap, 5L);
|
}else{
|
Object o = cacheMap.get(url);
|
if(null == o){
|
cacheMap.put(url, timestamp);
|
}else{
|
Long old_timestamp = Long.valueOf(o.toString());
|
Long new_timestamp = Long.valueOf(timestamp);
|
//两个请求时间差小于1秒,判定为重复提交
|
if((new_timestamp - old_timestamp) <= 1000){
|
throw new RuntimeException("重复提交");
|
}else{
|
cacheMap.put(url, timestamp);
|
}
|
}
|
redisService.setCacheMap(client, cacheMap, 5L);
|
}
|
}
|
|
|
|
/**
|
* 验证token
|
* @param request
|
* @throws Exception
|
*/
|
public void verifyToken(ServerHttpRequest request) throws Exception{
|
String token = getToken(request);
|
if (StringUtils.isEmpty(token)) {
|
throw new RuntimeException("令牌不能为空");
|
}
|
Claims claims = JwtUtils.parseToken(token);
|
if (claims == null) {
|
throw new RuntimeException("令牌已过期或验证不正确!");
|
}
|
String userid = JwtUtils.getUserId(claims);
|
if (StringUtils.isEmpty(userid)) {
|
throw new RuntimeException("令牌验证失败");
|
}
|
}
|
|
|
@Override
|
public int getOrder() {
|
return -300;
|
}
|
}
|