1、springcloud网关底层使用的是响应式+ServerHttpRequest。
2、网关主要作用是鉴权和转发。
3、一般拦截使用的都是实现GlobalFilter,重写它的filter方法。
4、对请求的header、param修改或者添加或者删除时用反射的方式实现。
比如如下代码
package org.springblade.gateway.filter;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.gateway.entity.RespRoleVO;
import org.springblade.gateway.entity.RespUserVO;
import org.springblade.gateway.props.AuthProperties;
import org.springblade.gateway.provider.AuthProvider;
import org.springblade.gateway.provider.RequestProvider;
import org.springblade.gateway.provider.ResponseProvider;
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.core.io.buffer.DataBuffer;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
/**
* 鉴权认证
*
* @author Base
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class AuthFilter implements GlobalFilter, Ordered {
private final AuthProperties authProperties;
private final ObjectMapper objectMapper;
private final JwtProperties jwtProperties;
@Value(value = "${avatar.role.platform}")
private String roleCode;
@Value("${avatar.role.checkUrl}")
private String checkUrl;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@SneakyThrows
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//校验 Token 放行
String originalRequestUrl = RequestProvider.getOriginalRequestUrl(exchange);
String path = exchange.getRequest().getURI().getPath();
// 判断是否是/oauth/token接口,是的话需要做avatar权限校验,获取用户
if ("/oauth/avatar/token".equals(path)) {
String avatarToken = exchange.getRequest().getQueryParams().getFirst("avatarToken");
// 取到avatarToken
if (StringUtils.isNotBlank(avatarToken)) {
// 通过avatarToken获取用户数据
RespUserVO userVO = avatarAuth(avatarToken);
ServerHttpResponse resp = exchange.getResponse();
if (null == userVO || null == userVO.getAccountId()) {
return unAuth(resp, "token失效,请重新传输!");
}
return avatarAuth(resp, userVO);
// 以反射的形式将用户名和密码存储到params中
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
Field targetMap = params.getClass().getDeclaredField("targetMap");
targetMap.setAccessible(true);
Map<String, String> newMap = new HashMap((Map) targetMap.get(params));
newMap.put("username", userVO.getAccountId());
newMap.put("password", md5DigestAsHex(DEFAULT_PASSWORD.getBytes(StandardCharsets.UTF_8)));
newMap.remove("avatarToken");
targetMap.set(params, newMap);
}
}
if (isSkip(path) || isSkip(originalRequestUrl)) {
return chain.filter(exchange);
}
//校验 Token 合法性
ServerHttpResponse resp = exchange.getResponse();
String headerToken = exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);
String paramToken = exchange.getRequest().getQueryParams().getFirst(AuthProvider.AUTH_KEY);
if (StringUtils.isBlank(headerToken) && StringUtils.isBlank(paramToken)) {
return unAuth(resp, "缺失令牌,鉴权失败");
}
String auth = StringUtils.isBlank(headerToken) ? paramToken : headerToken;
String token = JwtUtil.getToken(auth);
Claims claims = JwtUtil.parseJWT(token);
if (token == null || claims == null) {
return unAuth(resp, "请求未授权");
}
// 鉴权
String avatarToken = exchange.getRequest().getHeaders().getFirst(TokenConstant.AVATAR_TOKEN);
RespUserVO userVO = avatarAuth(avatarToken);
// 角色判断
if (!judgementRole(userVO)) {
return unAuth(resp, "鉴权失败!请重新申请授权");
}
//判断 Token 状态
if (jwtProperties.getState()) {
String tenantId = String.valueOf(claims.get(TokenConstant.TENANT_ID));
String userId = String.valueOf(claims.get(TokenConstant.USER_ID));
String accessToken = JwtUtil.getAccessToken(tenantId, userId, token);
if (!token.equalsIgnoreCase(accessToken)) {
return unAuth(resp, "令牌已失效");
}
}
return chain.filter(exchange);
}
/**
* 鉴权认证 通过avatarToken返回用户
*
* @param token 第三方JWTtoken
* @return boolean
*/
private RespUserVO avatarAuth(String token) {
String url = checkUrl + token;
RestTemplate template = new RestTemplate();
ResponseEntity<JSONObject> entity = template.exchange(url, HttpMethod.GET, null, JSONObject.class);
if (null != entity.getBody()) {
JSONObject object = entity.getBody();
HashMap<String, Object> mapUser = (HashMap<String, Object>) object.get("result");
RespUserVO respUserVO = new RespUserVO();
respUserVO.setAccountId((String) mapUser.get("accountId"));
respUserVO.setRoleVOList((List<RespRoleVO>) mapUser.get("roleVOList"));
return (null != respUserVO.getAccountId()) ? respUserVO : null;
}
return null;
}
/**
* 判断当前用户角色是否有平台权限
*
* @param userVO 用户
* @return boolean
*/
private Boolean judgementRole(RespUserVO userVO) {
if (null == userVO) {
return false;
}
List<RespRoleVO> voList = userVO.getRoleVOList();
if (null != voList) {
for (RespRoleVO role : voList) {
if (roleCode.equals(role.getRoleCode())) {
return true;
}
}
}
return false;
}
private boolean isSkip(String path) {
return AuthProvider.getDefaultSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path))
|| authProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
}
private Mono<Void> unAuth(ServerHttpResponse resp, String msg) {
resp.setStatusCode(HttpStatus.UNAUTHORIZED);
resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
String result = "";
try {
result = objectMapper.writeValueAsString(ResponseProvider.unAuth(msg));
} catch (JsonProcessingException e) {
log.error(e.getMessage(), e);
}
DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
return resp.writeWith(Flux.just(buffer));
}
/**
* 返回avatar用户的用户名
*
* @param resp 响应
* @param respUserVO 用户
*/
private Mono<Void> avatarAuth(ServerHttpResponse resp, RespUserVO respUserVO) {
resp.setStatusCode(HttpStatus.OK);
resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
String result = "";
try {
result = objectMapper.writeValueAsString(ResponseProvider.success(respUserVO.getAccountId()));
} catch (JsonProcessingException e) {
log.error(e.getMessage(), e);
}
DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
return resp.writeWith(Flux.just(buffer));
}
@Override
public int getOrder() {
return -100;
}
}
获取header和param
获取param: exchange.getRequest().getQueryParams().getFirst("avatarToken");
获取header: exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);
修改param
// 以反射的形式将用户名和密码存储到params中
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
Field targetMap = params.getClass().getDeclaredField("targetMap");
targetMap.setAccessible(true);
Map<String, String> newMap = new HashMap((Map) targetMap.get(params));
newMap.put("username", userVO.getAccountId());
newMap.put("password", md5DigestAsHex(DEFAULT_PASSWORD.getBytes(StandardCharsets.UTF_8)));
newMap.remove("avatarToken");
targetMap.set(params, newMap);







网友评论