You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
9.2 KiB
252 lines
9.2 KiB
package com.epmet.auth;
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.TypeReference;
|
|
import com.epmet.commons.tools.constant.AppClientConstant;
|
|
import com.epmet.commons.tools.constant.Constant;
|
|
import com.epmet.commons.tools.constant.ServiceConstant;
|
|
import com.epmet.commons.tools.dto.form.HasOperPermissionFormDTO;
|
|
import com.epmet.commons.tools.dto.result.OperResouce;
|
|
import com.epmet.commons.tools.exception.EpmetErrorCode;
|
|
import com.epmet.commons.tools.exception.EpmetException;
|
|
import com.epmet.commons.tools.exception.RenException;
|
|
import com.epmet.commons.tools.feign.CommonOperAccessOpenFeignClient;
|
|
import com.epmet.commons.tools.feign.ResultDataResolver;
|
|
import com.epmet.commons.tools.redis.RedisKeys;
|
|
import com.epmet.commons.tools.redis.RedisUtils;
|
|
import com.epmet.commons.tools.security.dto.BaseTokenDto;
|
|
import com.epmet.commons.tools.utils.CpUserDetailRedis;
|
|
import com.epmet.commons.tools.utils.Result;
|
|
import com.epmet.filter.CpProperty;
|
|
import com.epmet.jwt.JwtTokenUtils;
|
|
import io.jsonwebtoken.Claims;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
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.http.HttpHeaders;
|
|
import org.springframework.http.HttpMethod;
|
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.util.AntPathMatcher;
|
|
import org.springframework.web.server.ServerWebExchange;
|
|
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* 内部认证处理器
|
|
*/
|
|
@Component
|
|
public class InternalAuthProcessor extends AuthProcessor implements ResultDataResolver {
|
|
|
|
private Logger logger = LoggerFactory.getLogger(getClass());
|
|
|
|
@Autowired
|
|
private JwtTokenUtils jwtTokenUtils;
|
|
|
|
@Autowired
|
|
private CpUserDetailRedis cpUserDetailRedis;
|
|
|
|
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
|
|
|
|
@Autowired
|
|
private CpProperty cpProperty;
|
|
|
|
@Autowired
|
|
private CommonOperAccessOpenFeignClient operAccessOpenFeignClient;
|
|
|
|
@Autowired
|
|
private RedisUtils redisUtils;
|
|
|
|
@Override
|
|
public ServerWebExchange auth(ServerWebExchange exchange, GatewayFilterChain chain) {
|
|
ServerHttpRequest request = exchange.getRequest();
|
|
String requestUri = request.getPath().pathWithinApplication().value();
|
|
|
|
String token = getTokenFromRequest(request);
|
|
boolean needAuth = needAuth(requestUri);
|
|
|
|
if (needAuth && StringUtils.isBlank(token)) {
|
|
// token不能为空
|
|
throw new RenException(EpmetErrorCode.ERR10005.getCode(), EpmetErrorCode.ERR10005.getMsg());
|
|
}
|
|
|
|
BaseTokenDto baseTokenDto = null;
|
|
String app = "";
|
|
String client = "";
|
|
String userId = "";
|
|
String customerId = "";
|
|
Date expiration = null;
|
|
|
|
if(StringUtils.isNotBlank(token)){
|
|
//是否过期
|
|
Claims claims = jwtTokenUtils.getClaimByToken(token);
|
|
if (claims != null) {
|
|
app = (String) claims.get(AppClientConstant.APP);
|
|
client = (String) claims.get(AppClientConstant.CLIENT);
|
|
userId = (String) claims.get(AppClientConstant.USER_ID);
|
|
expiration = claims.getExpiration();
|
|
baseTokenDto = cpUserDetailRedis.get(app, client, userId, BaseTokenDto.class);
|
|
}
|
|
}
|
|
|
|
if (baseTokenDto != null) {
|
|
customerId = baseTokenDto.getCustomerId();
|
|
}
|
|
|
|
if (needAuth) {
|
|
validateToken(baseTokenDto, token, expiration);
|
|
}
|
|
|
|
// 添加header
|
|
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
|
|
if (StringUtils.isNotBlank(app)) {
|
|
builder.header(AppClientConstant.APP, app);
|
|
}
|
|
if (StringUtils.isNotBlank(client)) {
|
|
builder.header(AppClientConstant.CLIENT, client);
|
|
}
|
|
if (StringUtils.isNotBlank(userId)) {
|
|
builder.header(AppClientConstant.USER_ID, userId);
|
|
}
|
|
|
|
if (baseTokenDto != null) {
|
|
String redisKey = baseTokenDto.getApp() + "-" + baseTokenDto.getClient() + "-" + baseTokenDto.getUserId();
|
|
logger.info("redisKey=" + redisKey);
|
|
|
|
builder.header(Constant.APP_USER_KEY, redisKey);
|
|
}
|
|
|
|
if(StringUtils.isNotBlank(customerId)){
|
|
builder.header(AppClientConstant.CUSTOMER_ID, customerId);
|
|
}
|
|
|
|
// 针对运营端的url拦截和校验
|
|
if (AppClientConstant.APP_OPER.equals(app)) {
|
|
HttpMethod method = request.getMethod();
|
|
Boolean hasAccess = checkRequestOperResource(userId, requestUri, method.toString());
|
|
if (!hasAccess) {
|
|
throw new EpmetException(EpmetErrorCode.EPMET_COMMON_OPERATION_FAIL.getCode(), "资源未授权", "资源未授权");
|
|
}
|
|
}
|
|
|
|
ServerHttpRequest shr = builder.build();
|
|
return exchange.mutate().request(shr).build();
|
|
}
|
|
|
|
/**
|
|
* 校验运营端用户是否有权访问该资源
|
|
* @param uri
|
|
* @param method
|
|
* @return
|
|
*/
|
|
private Boolean checkRequestOperResource(String userId, String uri, String method) {
|
|
String resourceJsonString = redisUtils.getString(RedisKeys.getOperExamineResourceUrls());
|
|
List<OperResouce> resources = JSON.parseObject(resourceJsonString, new TypeReference<List<OperResouce>>() {});
|
|
|
|
if (resources == null) {
|
|
// redis中没有缓存,需要api获取
|
|
resources = getResultDataOrThrowsException(operAccessOpenFeignClient.getExamineResourceUrls(), ServiceConstant.OPER_ACCESS_SERVER,
|
|
EpmetErrorCode.SERVER_ERROR.getCode(), "调用operaccess获取要校验的资源失败", "调用operaccess获取要校验的资源失败");
|
|
|
|
// 缓存
|
|
redisUtils.setString(RedisKeys.getOperExamineResourceUrls(), JSON.toJSONString(resources));
|
|
}
|
|
|
|
for (OperResouce resource : resources) {
|
|
if (antPathMatcher.match(resource.getResourceUrl(), uri)
|
|
&& resource.getResourceMethod().equals(method)) {
|
|
|
|
//需要校验权限的url
|
|
HasOperPermissionFormDTO form = new HasOperPermissionFormDTO();
|
|
form.setUri(uri);
|
|
form.setMethod(method);
|
|
form.setOperId(userId);
|
|
Result result = operAccessOpenFeignClient.hasOperPermission(form);
|
|
if (result == null || !result.success()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// 如果当前请求url不需要校验权限,那么返回true
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 是否需要认证
|
|
* @param requestUri
|
|
* @return
|
|
*/
|
|
private boolean needAuth(String requestUri) {
|
|
for (String url : cpProperty.getInternalAuthUrlsWhiteList()) {
|
|
if (antPathMatcher.match(url, requestUri)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (String url : cpProperty.getInternalAuthUrls()) {
|
|
if (antPathMatcher.match(url, requestUri)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 从请求中获取token
|
|
* @param request
|
|
* @return
|
|
*/
|
|
private String getTokenFromRequest(ServerHttpRequest request) {
|
|
HttpHeaders headers = request.getHeaders();
|
|
String token = headers.getFirst(Constant.AUTHORIZATION_HEADER);
|
|
if (StringUtils.isBlank(token)) {
|
|
token = headers.getFirst(Constant.TOKEN_HEADER);
|
|
}
|
|
if (StringUtils.isBlank(token)) {
|
|
token = request.getQueryParams().getFirst(Constant.AUTHORIZATION_HEADER);
|
|
}
|
|
return token;
|
|
}
|
|
|
|
/**
|
|
* @Description 从用户token中取app,client,userId三项数据
|
|
* @return
|
|
* @author wxz
|
|
* @date 2021.06.11 15:04
|
|
*/
|
|
private BaseTokenDto getBaseTokenDto(String token, JwtTokenUtils jwtTokenUtils) {
|
|
//是否过期
|
|
Claims claims = jwtTokenUtils.getClaimByToken(token);
|
|
if (claims == null || jwtTokenUtils.isTokenExpired(claims.getExpiration())) {
|
|
return null;
|
|
}
|
|
//获取用户ID
|
|
String app = (String) claims.get("app");
|
|
String client = (String) claims.get("client");
|
|
String userId = (String) claims.get("userId");
|
|
return new BaseTokenDto(app, client, userId, token);
|
|
}
|
|
|
|
/**
|
|
* 校验Token是否异常
|
|
* @param tokenDto
|
|
* @param tokenStr
|
|
*/
|
|
private void validateToken(BaseTokenDto tokenDto, String tokenStr, Date expiration) {
|
|
if (null == tokenDto || jwtTokenUtils.isTokenExpired(expiration)) {
|
|
//说明登录状态时效(超时)
|
|
throw new RenException(EpmetErrorCode.ERR10006.getCode(), EpmetErrorCode.ERR10006.getMsg());
|
|
}else{
|
|
//Redis中存在数据,取出token,进行比对
|
|
if(!StringUtils.equals(tokenDto.getToken(),tokenStr)){
|
|
//用户携带token与Redis中不一致,说明当前用户此次会话失效,提示重新登陆
|
|
throw new RenException(EpmetErrorCode.ERR10007.getCode(), EpmetErrorCode.ERR10007.getMsg());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|