package com.xiaopeng.gateway.filter;
|
|
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSONObject;
|
import com.xiaopeng.common.core.exception.auth.NotLoginException;
|
import com.xiaopeng.gateway.config.properties.AntiShakeProperties;
|
import org.apache.commons.lang3.RandomStringUtils;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Value;
|
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.xiaopeng.common.core.constant.CacheConstants;
|
import com.xiaopeng.common.core.constant.HttpStatus;
|
import com.xiaopeng.common.core.constant.SecurityConstants;
|
import com.xiaopeng.common.core.constant.TokenConstants;
|
import com.xiaopeng.common.core.utils.JwtUtils;
|
import com.xiaopeng.common.core.utils.ServletUtils;
|
import com.xiaopeng.common.core.utils.StringUtils;
|
import com.xiaopeng.common.redis.service.RedisService;
|
import com.xiaopeng.gateway.config.properties.IgnoreWhiteProperties;
|
import io.jsonwebtoken.Claims;
|
import reactor.core.publisher.Mono;
|
|
import java.util.HashMap;
|
import java.util.Map;
|
import java.util.UUID;
|
|
|
/**
|
* 网关鉴权
|
*
|
* @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 AntiShakeProperties antiShakeProperties;
|
|
@Autowired
|
private RedisService redisService;
|
|
@Value("${xiaopeng.xtiger.sso.issuer}")
|
private String issuer;
|
|
@Value("${xiaopeng.xtiger.sso.appId}")
|
private String appId;
|
|
@Value("${xiaopeng.xtiger.sso.serverHost}")
|
private String serverHost;
|
|
|
|
|
@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 {
|
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);
|
String userType = JwtUtils.getUserType(claims);
|
String operationUserKey = JwtUtils.getOperationUserKey(claims);
|
if (!checkPathPermission(userType, url)) {
|
return unauthorizedResponse(exchange, "无权访问当前资源");
|
}
|
String redisKey = "";
|
if ("system".equals(userType)) {
|
redisKey = StringUtils.isNotBlank(userkey) ? getTokenKey(userkey) : "";
|
}
|
if ("operation".equals(userType)) {
|
redisKey = StringUtils.isNotBlank(operationUserKey) ? getTokenKey(operationUserKey) : "";
|
}
|
if (StringUtils.isBlank(redisKey)) {
|
return unauthorizedResponse(exchange, "登录状态已过期");
|
}
|
|
boolean islogin = redisService.hasKey(redisKey);
|
if (!islogin)
|
{
|
return unauthorizedResponse(exchange, "登录状态已过期");
|
}
|
// 设置用户信息到请求
|
addHeader(mutate, SecurityConstants.USER_KEY, userkey);
|
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid);
|
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);
|
addHeader(mutate, SecurityConstants.USER_TYPE, userType);
|
addHeader(mutate, SecurityConstants.OPERATION_USER_KEY, operationUserKey);
|
// 内部请求来源参数清除
|
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);
|
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 ServletUtils.webFluxResponseWriter(exchange.getResponse(), jsonObject.toString(), HttpStatus.UNAUTHORIZED);
|
}
|
|
|
/**
|
* 获取缓存key
|
*/
|
private String getTokenKey(String token) {
|
return CacheConstants.LOGIN_TOKEN_KEY + token;
|
}
|
|
private boolean checkPathPermission(String userType, String url) {
|
boolean appPath = StringUtils.isNotBlank(url) && url.contains("/maintenance-app");
|
if ("operation".equals(userType)) {
|
return appPath;
|
}
|
if ("system".equals(userType)) {
|
return !appPath;
|
}
|
return true;
|
}
|
|
/**
|
* 获取请求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;
|
}
|
|
|
|
|
/**
|
* 验证token
|
* @param request
|
* @throws Exception
|
*/
|
public void verifyToken(ServerHttpRequest request) throws Exception{
|
String token = getToken(request);
|
if (StringUtils.isEmpty(token)) {
|
throw new NotLoginException("令牌不能为空");
|
}
|
Claims claims = JwtUtils.parseToken(token);
|
if (claims == null) {
|
throw new NotLoginException("令牌已过期或验证不正确!");
|
}
|
String userid = JwtUtils.getUserId(claims);
|
if (StringUtils.isEmpty(userid)) {
|
throw new NotLoginException("令牌验证失败");
|
}
|
}
|
|
|
@Override
|
public int getOrder() {
|
return -300;
|
}
|
}
|