提交 d7673dfb authored 作者: 陈世营's avatar 陈世营

spring-gateway

上级
package com.loit.getway.config;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* @author wbh
* @createDate 2020-03-23
*/
@Component
@ConfigurationProperties(prefix = "auth-skip")
public class AuthSkipUrlsProperties implements InitializingBean {
private static final String NORMAL = "(\\w|\\d|-)+";
private List<Pattern> urlPatterns = new ArrayList<>();
private List<Pattern> serverPatterns = new ArrayList<>();
private List<String> dataWorksServers;
private List<String> apiUrls;
private List<String> noUserServers;
private List<String> noUserUri;
private List<String> commonUri;
/**
* This {@link AuthSkipUrlsProperties} init
*/
@Override
public void afterPropertiesSet() {
dataWorksServers.stream().map(d -> d.replace("*", NORMAL)).map(Pattern::compile).forEach(serverPatterns::add);
apiUrls.stream().map(s -> s.replace("*", NORMAL)).map(Pattern::compile).forEach(urlPatterns::add);
}
public List<Pattern> getUrlPatterns() {
return urlPatterns;
}
public void setUrlPatterns(List<Pattern> urlPatterns) {
this.urlPatterns = urlPatterns;
}
public List<Pattern> getServerPatterns() {
return serverPatterns;
}
public void setServerPatterns(List<Pattern> serverPatterns) {
this.serverPatterns = serverPatterns;
}
public List<String> getDataWorksServers() {
return dataWorksServers;
}
public void setDataWorksServers(List<String> dataWorksServers) {
this.dataWorksServers = dataWorksServers;
}
public List<String> getApiUrls() {
return apiUrls;
}
public void setApiUrls(List<String> apiUrls) {
this.apiUrls = apiUrls;
}
public List<String> getNoUserServers() {
return noUserServers==null?new ArrayList<>():noUserServers;
}
public void setNoUserServers(List<String> noUserServers) {
this.noUserServers = noUserServers;
}
public List<String> getNoUserUri() {
return noUserUri==null?new ArrayList<>():noUserUri;
}
public void setNoUserUri(List<String> noUserUri) {
this.noUserUri = noUserUri;
}
public List<String> getCommonUri() {
return commonUri==null?new ArrayList<>():commonUri;
}
public void setCommonUri(List<String> commonUri) {
this.commonUri = commonUri;
}
}
\ No newline at end of file
package com.loit.getway.config;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator;
import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.support.DefaultServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* 跨域允许
*/
@Configuration
public class CrossOriginFilter {
private static final String MAX_AGE = "18000L";
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
HttpHeaders requestHeaders = request.getHeaders();
ServerHttpResponse response = ctx.getResponse();
HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
HttpHeaders headers = response.getHeaders();
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders
.getAccessControlRequestHeaders());
if(requestMethod != null){
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
}
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
}
// @Bean
// public ServerCodecConfigurer serverCodecConfigurer() {
// return new DefaultServerCodecConfigurer();
// }
/**
* 如果使用了注册中心(如:Eureka),进行控制则需要增加如下配置
*/
// @Bean
// public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient,
// DiscoveryLocatorProperties properties) {
// return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
// }
}
package com.loit.getway.config;
import java.net.URI;
import java.util.List;
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.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
@Component
@ConfigurationProperties(prefix = "loit.cas.gateway")
public class GateWayConfig {
private static String SERVICE_NAME_PORTAL;
// /**
// * cas服务器ip+port
// */
// private String serverUrl;
protected Logger logger = LoggerFactory.getLogger(getClass());
/**
* 资源鉴权开关
*/
private Boolean resourceAuth;
/**
* 登录鉴权开关
*/
private Boolean loginAuth;
/**
* swagger开关
*/
private Boolean swaggerClose;
public Boolean getSwaggerClose() {
return swaggerClose;
}
public void setSwaggerClose(Boolean swaggerClose) {
this.swaggerClose = swaggerClose;
}
/**
* 登录过期时间,单位秒
*/
private int killSeconds;
public static String hostIp;
/**
* 获取用户信息,报901则需登录
* @return
*/
public String profileUrl(ServerHttpRequest request){
return getCasServerUrl() + "/cas/oauth2.0/profile?access_token={accessToken}";
}
@Autowired
private GateWayRoutesYml gateWayRoutesYml;
public static String caseServerPath;
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
/**
* 获取Cas单点登录的服务转发路径
* @return
*/
public String getCasServerUrl() {
for (GateWayService gateWayService : gateWayRoutesYml.getRoutes()) {
if(gateWayService.getCasServerSign()!=null) {
SERVICE_NAME_PORTAL = gateWayService.getId();
return "http://"+SERVICE_NAME_PORTAL;
}
}
return null;
}
public Boolean getResourceAuth() {
return resourceAuth;
}
public void setResourceAuth(Boolean resourceAuth) {
this.resourceAuth = resourceAuth;
}
public Boolean getLoginAuth() {
return loginAuth;
}
public void setLoginAuth(Boolean loginAuth) {
this.loginAuth = loginAuth;
}
public int getKillSeconds() {
return killSeconds;
}
public void setKillSeconds(int killSeconds) {
this.killSeconds = killSeconds;
}
}
package com.loit.getway.config;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 获取网关的服务列表
* @author wbh12
*
*/
@Component
@ConfigurationProperties(prefix = "spring.cloud.gateway")
public class GateWayRoutesYml {
List<GateWayService> routes;
public List<GateWayService> getRoutes() {
return routes;
}
public void setRoutes(List<GateWayService> routes) {
this.routes = routes;
}
}
package com.loit.getway.config;
import java.util.List;
public class GateWayService {
private String id;
private String uri;
private List<String> predicates;
private List<String> filters;
private String logoutSign;
private String casServerSign;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public List<String> getPredicates() {
return predicates;
}
public void setPredicates(List<String> predicates) {
this.predicates = predicates;
}
public List<String> getFilters() {
return filters;
}
public void setFilters(List<String> filters) {
this.filters = filters;
}
public String getLogoutSign() {
return logoutSign;
}
public void setLogoutSign(String logoutSign) {
this.logoutSign = logoutSign;
}
public String getCasServerSign() {
return casServerSign;
}
public void setCasServerSign(String casServerSign) {
this.casServerSign = casServerSign;
}
}
/*
*
* Copyright (c) 2014-2020, yugenhai108@gmail.com.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package com.loit.getway.config;
import java.io.Serializable;
public class GatewayContext implements Serializable {
private static final long serialVersionUID = 7420632808330120030L;
private boolean doNext = true;
private boolean black;
private String userToken;
private String path;
private String redirectUrl;
public boolean isDoNext() {
return doNext;
}
public void setDoNext(boolean doNext) {
this.doNext = doNext;
}
public boolean isBlack() {
return black;
}
public void setBlack(boolean black) {
this.black = black;
}
public String getUserToken() {
return userToken;
}
public void setUserToken(String userToken) {
this.userToken = userToken;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getRedirectUrl() {
return redirectUrl;
}
public void setRedirectUrl(String redirectUrl) {
this.redirectUrl = redirectUrl;
}
}
package com.loit.getway.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* restTemplate负载设置
* @author wbh12
* @date: 2020年5月6日
* add by wbh
*/
@Configuration
public class RibbonConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
package com.loit.getway.constant;
/**
* @ClassName ClientLoitStatusCode
* @Description: 单点登录客户端状态码 编码:3位状态码中,第一位代表一个应用
* @Author gzx
* @Date 2019-10-17
* @Version V1.0
**/
public enum ClientLoitStatusCode {
SUCESS("100","成功"),
FAIL("101","失败"),
CAS_USER_TOKEN_FAIL("901", "无权限访问该资源,令牌无效或不存在"),
CAS_AUTHENTICATION_FAIL("903", "无权限访问该资源"),
;
/**
* 状态编码号
*/
public String statusCode;
/**
* 状态描述
*/
public String statusDesc;
/**
* 构造函数
* @param statusCode
* @param statusDesc
*/
ClientLoitStatusCode(String statusCode, String statusDesc){
this.statusCode = statusCode;
this.statusDesc = statusDesc;
}
}
package com.loit.getway.constant;
/**
* 网关通用常量
*
* @author wbh
* add by 2020-02-15
*/
public interface CommonConstant {
/**
* 令牌
*/
String TOKEN_NAME = "accessToken";
/**
* 用户信息
*/
String AUTHORIZATION = "Authorization";
/**
* 在线用户
*/
String ONLINE_USERS = "onlineUsers";
}
package com.loit.getway.controller;
import com.loit.common.json.AjaxJson;
import com.loit.getway.config.GateWayConfig;
import com.loit.getway.constant.CommonConstant;
import com.loit.getway.utils.RedisUtil;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Set;
@RestController
@RequestMapping(value = "/online/user")
@Api(tags = "在线用户数量统计", value = "OnlineUserCountController")
public class OnlineUserCountController {
@Autowired
private GateWayConfig gateWayConfig;
/**
* 登录,设置用户在线时间
* @return
*/
@GetMapping(value = "/save")
public void saveOnline(@RequestParam(value = "userId", required = true) String userId){
Calendar now = Calendar.getInstance();
now.set(Calendar.SECOND, now.get(Calendar.SECOND) + gateWayConfig.getKillSeconds());
RedisUtil.zAdd(CommonConstant.ONLINE_USERS, userId, now.getTimeInMillis());
}
/**
* 登出,删除在线用户信息
* @return
*/
@GetMapping(value = "/delete")
public void deleteOnline(@RequestParam(value = "userId", required = true) String userId){
RedisUtil.zRemove(CommonConstant.ONLINE_USERS, userId);
}
/**
* 获取在线用户数量
* @return
*/
@GetMapping(value = "/count")
public AjaxJson<Long> onlineUserCount(){
AjaxJson json = new AjaxJson();
Long count = RedisUtil.zCard(CommonConstant.ONLINE_USERS);
json.put("count", count);
return json;
}
/**
* 获取在线用户列表
* @return
*/
@GetMapping(value = "/list")
public AjaxJson<List<String>> onlineUserList(){
AjaxJson json = new AjaxJson();
Set<String> ids = RedisUtil.zRange(CommonConstant.ONLINE_USERS);
json.put("list", new ArrayList(ids));
return json;
}
}
package com.loit.getway.dto;
public class ClientAccessTokenUserDTO {
private String id;
private Attributes attributes;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Attributes getAttributes() {
return attributes;
}
public void setAttributes(Attributes attributes) {
this.attributes = attributes;
}
private static class Attributes {
private String credentialType;
public String getCredentialType() {
return credentialType;
}
public void setCredentialType(String credentialType) {
this.credentialType = credentialType;
}
}
}
package com.loit.getway.filter;
import com.loit.getway.config.GateWayConfig;
import com.loit.getway.constant.CommonConstant;
import com.loit.getway.utils.CacheUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.InetAddress;
@Configuration
public class GetwayLogFilter implements GlobalFilter, Ordered {
protected Logger logger = LoggerFactory.getLogger(GetwayLogFilter.class);
private static final String REQUEST_TIME_BEGIN = "beginTime";
private final static String USER_ID = "userId";
private final static String QUERY_STRING = "queryString";
private final static String HOST_IP = "hostIp";
private final static String HOST_PORT = "hostPort";
private final static String REQ_URI="reqUri";
private final static String REQ_TYPE="reqType";
@Override
public int getOrder() {
// TODO Auto-generated method stub
return -1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
insertIntoMDC(exchange.getRequest());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
long timeSlot = System.currentTimeMillis() - startTime;
String timeDesc = "time consuming";
if(timeSlot>1000&&timeSlot<= 5000) {
timeDesc = "long time consuming";
}else if(timeSlot>5000) {
timeDesc = "super long time consuming";
}
if (startTime != null) {
logger.info("URI: {} 最大内存: {}m 已分配内存: {}m 已分配内存中的剩余空间: {}m 最大可用内存: {}m "+timeDesc+": {}ms",
exchange.getRequest().getURI().getRawPath(), Runtime.getRuntime().maxMemory()/1024/1024, Runtime.getRuntime().totalMemory()/1024/1024, Runtime.getRuntime().freeMemory()/1024/1024,
(Runtime.getRuntime().maxMemory()-Runtime.getRuntime().totalMemory()+Runtime.getRuntime().freeMemory())/1024/1024,timeSlot);
}
})
);
}
/**
* @Description: TODO(将请求相关信息存放到MDC中,以便在logback日志中输出)
* @author yangwenbin
* @date
*/
private void insertIntoMDC(ServerHttpRequest request) {
//获取服务IP
String hostIp = GateWayConfig.hostIp;
//获取服务端口
String hostPort = String.valueOf(request.getURI().getPort());
//获取参数
String queryString=request.getQueryParams().toString();
//请求地址
String reqUri=request.getURI().getPath();
//请求方式
String reqType = request.getMethodValue();
HttpCookie httpCookie = request.getCookies().getFirst(CommonConstant.TOKEN_NAME);
String accessToken="";
if(null == httpCookie || org.springframework.util.StringUtils.isEmpty(httpCookie)) {
accessToken = request.getHeaders().getFirst(CommonConstant.TOKEN_NAME);
}else {
accessToken = httpCookie.getValue();
}
String userId = "";
//用户ID
if(StringUtils.isNotEmpty(accessToken)){
userId = (String) CacheUtils.get(accessToken);
}
//以下是存放相关信息到MDC中
MDC.put(HOST_IP,hostIp);
MDC.put(HOST_PORT,hostPort);
MDC.put(QUERY_STRING,queryString);
MDC.put(REQ_URI,reqUri);
MDC.put(REQ_TYPE,reqType);
MDC.put(USER_ID,userId);
}
// /**
// * @Description: TODO(获取服务IP地址)
// * @author yangwenbin
// * @date
// */
// private String getHostIp() {
// try {
// InetAddress address = InetAddress.getLocalHost();
// return address.getHostAddress();
//
// } catch (Exception e) {
// logger.error("获取服务IP地址异常{}",e);
// }
// return null;
// }
}
package com.loit.getway.init;
import com.loit.getway.config.GateWayConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.net.InetAddress;
/**
* Description: 应用启动初始化加载数据
* Author: yangwenbin
* Date: Created in 2020/8/6 14:49
* Version: 1.0
* Modified By:
*/
@Component
public class LoitStrtupRunner implements ApplicationRunner {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void run(ApplicationArguments args) throws Exception {
//此处因为在日志每次获取服务Ip,netAddress.getLocalHost()为同步代码会导致性能超低,将此代码移动到服务启动加载,设置全局变量以供使用
InetAddress address = InetAddress.getLocalHost();
GateWayConfig.hostIp = address.getHostAddress();
logger.info("获取本服务ip地址为:{}",GateWayConfig.hostIp);
}
}
package com.loit.getway.json;
import java.io.Serializable;
import java.util.LinkedHashMap;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.loit.getway.constant.ClientLoitStatusCode;
import com.loit.getway.utils.JsonMapper;
/**
* $.ajax后需要接受的JSON
*
* @author
*
*/
public class ClientAjaxJson implements Serializable{
public static final String CODE_COMMON_FAIL = ClientLoitStatusCode.FAIL.statusCode;
/**
* @author wbh
* @date 2018年12月11日
*/
private static final long serialVersionUID = -7790919285662399570L;
private boolean success = true;// 是否成功
private String code = ClientLoitStatusCode.SUCESS.statusCode;//返回码 100-成功,101失败,其他返回码参见LoitStatusCode
private String msg = "操作成功";// 提示信息
private LinkedHashMap<String, Object> body = new LinkedHashMap<String, Object>();//封装json的map
public LinkedHashMap<String, Object> getBody() {
return body;
}
public void setBody(LinkedHashMap<String, Object> body) {
this.body = body;
}
public void put(String key, Object value){//向json中添加属性,在js中访问,请调用data.map.key
body.put(key, value);
}
public void remove(String key){
body.remove(key);
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {//向json中添加属性,在js中访问,请调用data.msg
this.msg = msg;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
@JsonIgnore//返回对象时忽略此属性
public String getJsonStr() {//返回json字符串数组,将访问msg和key的方式统一化,都使用data.key的方式直接访问。
String json = JsonMapper.getInstance().toJson(this);
return json;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toString() {
return "AjaxJson [success=" + success + ", code=" + code + ", msg=" + msg + "]";
}
/**
* 返回普通异常信息。code=101
* @author wbh
* @date:2018年12月16日
* @param info
* @return
*/
public static ClientAjaxJson returnExceptionInfo(String info) {
ClientAjaxJson json = new ClientAjaxJson();
json.setSuccess(false);
json.setCode(CODE_COMMON_FAIL);
json.setMsg(info);
return json;
}
/**
* 返回普通异常信息。code=101
* @author wbh
* @date:2018年12月16日
* @param info
* @return
*/
public static ClientAjaxJson returnExceptionInfo(String info, ClientAjaxJson json) {
json.setSuccess(false);
json.setCode(CODE_COMMON_FAIL);
json.setMsg(info);
return json;
}
/**
* 返回指定异常信息。
* @author wbh
* @date:2019年10月31日
* @param code 状态码
* @param info 返回信息
* @return
*/
public static ClientAjaxJson returnExceptionInfo(ClientLoitStatusCode info) {
ClientAjaxJson json = new ClientAjaxJson();
json.setSuccess(false);
json.setCode(info.statusCode);
json.setMsg(info.statusDesc);
return json;
}
/**
* 返回指定异常信息。
* @author wbh
* @date:2019年10月31日
* @param code 状态码
* @param info 返回信息
* @return
*/
public static ClientAjaxJson returnExceptionInfo(ClientLoitStatusCode info, ClientAjaxJson json) {
json.setSuccess(false);
json.setCode(info.statusCode);
json.setMsg(info.statusDesc);
return json;
}
}
package com.loit.getway.schedule;
import com.loit.getway.constant.CommonConstant;
import com.loit.getway.utils.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* @description: 在线用户定时器
* @date: 2020/7/6 9:43
* @author: wubj
* @version v1.0
*/
@Component
public class OnlineUserSchedule {
private Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 定时任务:删除不在线用户数据
*/
@Scheduled(cron = "0 0/3 * * * ?")
public void scheduledTask() {
log.info("删除不在线用户数据开始执行");
long begin = System.currentTimeMillis();
// 删除过了有效期的数据
RedisUtil.removeRangeByScore(CommonConstant.ONLINE_USERS, 0.0, Double.valueOf(System.currentTimeMillis()));
long end = System.currentTimeMillis();
log.info("删除不在线用户数据执行结束,花费时间为" + (end - begin) + "毫秒");
}
}
package com.loit.getway.swagger.config;
import com.loit.getway.config.GateWayConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: TODO(GateWay 获取SwaggerWay Api-doc)
* @author yangwenbin
* @date 2020-04-02
*/
@Component
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs";
private RouteLocator routeLocator;
private GatewayProperties gatewayProperties;
@Autowired
private GateWayConfig gateWayConfig;
public SwaggerProvider(RouteLocator routeLocator, GatewayProperties gatewayProperties){
this.gatewayProperties = gatewayProperties;
this.routeLocator =routeLocator;
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
if(gateWayConfig.getSwaggerClose() == null || !gateWayConfig.getSwaggerClose()){
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("/**", API_URI)))));
}
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
package com.loit.getway.swagger.filter;
import com.loit.getway.swagger.config.SwaggerProvider;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
/**
* @Description: TODO(GateWay 集成Swagger中会根据X-Forwarded-Prefix这个Header来获取BasePath,将它添加至接口路径与host中间,否则会404)
* @author yangwenbin
* @date 2020-04-02
*/
@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
private static final String HEADER_NAME = "X-Forwarded-Prefix";
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) {
return chain.filter(exchange);
}
String basePath = path.substring(0, path.lastIndexOf(SwaggerProvider.API_URI));
ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
};
}
}
\ No newline at end of file
package com.loit.getway.swagger.handle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;
import java.util.Optional;
/**
* @Description: TODO(GateWay集成Swagger Handle处理)
* @author yangwenbin
* @date 2020-04-02
*/
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerHandle {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResources;
@Autowired
public SwaggerHandle(SwaggerResourcesProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
@GetMapping("/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
/**
* Copyright &copy; 2015-2020 isaac All rights reserved.
*/
package com.loit.getway.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Cache工具类
* @author zpc
* @version 2013-5-29
* update by wbh 2018-08-24 更改为redis服务获取缓存
*/
public class CacheUtils {
protected static long SECONDS = 3000;
protected static Logger logger = LoggerFactory.getLogger(CacheUtils.class);
/**
* 获取SYS_CACHE缓存
* @param key
* @return
*/
public static Object get(String key) {
Object result = RedisUtil.get(key);
return result;
}
/**
* 写入SYS_CACHE缓存
* @param key
* @return
*/
public static void put(String key, Object value) {
RedisUtil.set(key, value,SECONDS);
// put(SYS_CACHE, key, value);
}
/**
* 从SYS_CACHE缓存中移除
* @param key
* @return
*/
public static void remove(String key) {
RedisUtil.remove(key);
// remove(SYS_CACHE, key);
}
/**
* 获取缓存
* @param cacheName
* @param key
* @return
*/
public static Object get(String cacheName, String key) {
Object result = RedisUtil.get(key);
// logger.info("获取("+key+")的缓存:"+result);
// Element element = getCache(cacheName).get(key);
// return element==null?null:element.getObjectValue();
return result;
}
/**
* 写入缓存
* @param cacheName
* @param key
* @param value
*/
public static void put(String cacheName, String key, Object value) {
RedisUtil.set(key, value,SECONDS);
// logger.info("设置key为"+key+"的缓存:"+value);
// Element element = new Element(key, value);
// getCache(cacheName).put(element);
}
/**
* 写入缓存
* @param expireTime 生存时效 单位:秒
* @param key
* @param value
*/
public static void set(String key, Object value, Long expireTime) {
RedisUtil.set(key, value, expireTime);
// logger.info("设置key为"+key+"的缓存:"+value);
// Element element = new Element(key, value);
// getCache(cacheName).put(element);
}
/**
* 从缓存中移除
* @param cacheName
* @param key
*/
public static void remove(String cacheName, String key) {
RedisUtil.remove(key);
// getCache(cacheName).remove(key);
}
/**
* 获得一个Cache,没有则创建一个。
* @param cacheName
* @return
*/
/*private static Cache getCache(String cacheName){
Cache cache = cacheManager.getCache(cacheName);
if (cache == null){
cacheManager.addCache(cacheName);
cache = cacheManager.getCache(cacheName);
cache.getCacheConfiguration().setEternal(true);
}
return cache;
}
public static CacheManager getCacheManager() {
return cacheManager;
}*/
}
package com.loit.getway.utils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.loit.getway.config.GateWayConfig;
import com.loit.getway.dto.ClientAccessTokenUserDTO;
@Component("clientUserUtil")
public class ClientUserUtil {
@Autowired
private RestTemplate restTemplate;
@Autowired
private GateWayConfig gateWayConfig;
private final Long defaultAccessTokenKillTimeOut = 3600L;
protected Logger logger = LoggerFactory.getLogger(getClass());
/**
* 获取登录用户id-从登录服务器获取
*/
public String getCasServerUserId(ServerHttpRequest request, String accessToken){
logger.debug("3.网关获取的令牌={}", accessToken);
if(accessToken==null||accessToken.length()==0) {
return null;
}
long startTime=System.currentTimeMillis();
// PC端和移动端accessToken 在第一次从单点获取用户信息时做了缓存,此处可以直接从缓存获取accessToken取得相应的userId
String userId = (String) CacheUtils.get(accessToken);
logger.debug("4.缓存获取userId={}",userId);
if(StringUtils.isNotEmpty(userId)){
//读取单点配置的accessToken过期时间
Object timeObject = RedisUtil.get("CAS_ACCESS_TOKEN_TIME_OUT");
Long timeOut = timeObject != null ? Long.parseLong(timeObject.toString()) : defaultAccessTokenKillTimeOut;
}
if(StringUtils.isNotEmpty(userId)){
return userId;
}
String url = gateWayConfig.profileUrl(request).replace("{accessToken}", accessToken);
logger.debug("6.登录服务器获取userId的url={}", url);
ClientAccessTokenUserDTO accessTokenUserDTO = restTemplate.getForObject(url, ClientAccessTokenUserDTO.class);
userId = accessTokenUserDTO.getId();
logger.debug("7.登录服务器获取userId={}", userId);
long endTime=System.currentTimeMillis();
float excTime=(float)(endTime-startTime);
logger.info("userId: {} 耗时: {}ms 获取userId的url: {}", userId, excTime, url);
return userId;
}
}
package com.loit.getway.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "loit")
public class GateWayLoitLog {
protected Logger logger = LoggerFactory.getLogger(getClass());
private Boolean gatewayDebugOut;
public void info(String info) {
if(gatewayDebugOut!=null&&gatewayDebugOut)
logger.info(info);
}
public Boolean getGatewayDebugOut() {
return gatewayDebugOut;
}
public void setGatewayDebugOut(Boolean gatewayDebugOut) {
this.gatewayDebugOut = gatewayDebugOut;
}
}
package com.loit.getway.utils;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* 简单封装Jackson,实现JSON String<->Java Object的Mapper.
* 封装不同的输出风格, 使用不同的builder函数创建实例.
* @author loit
* @version 2013-11-15
*/
@SuppressWarnings("deprecation")
public class JsonMapper extends ObjectMapper {
private static final long serialVersionUID = 1L;
private static Logger logger = LoggerFactory.getLogger(JsonMapper.class);
private static JsonMapper mapper;
public JsonMapper() {
//this(Include.NON_EMPTY);
}
public JsonMapper(Include include) {
// 设置输出时包含属性的风格
if (include != null) {
this.setSerializationInclusion(include);
}
// 允许单引号、允许不带引号的字段名称
this.enableSimple();
// 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 空值处理为空串
this.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>(){
@Override
public void serialize(Object value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeString("");
}
});
// 进行HTML解码。
this.registerModule(new SimpleModule().addSerializer(String.class, new JsonSerializer<String>(){
@Override
public void serialize(String value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeString(StringEscapeUtils.unescapeHtml4(value));
}
}));
// 设置时区
this.setTimeZone(TimeZone.getDefault());//getTimeZone("GMT+8:00")
}
/**
* 创建只输出非Null且非Empty(如List.isEmpty)的属性到Json字符串的Mapper,建议在外部接口中使用.
*/
public static JsonMapper getInstance() {
if (mapper == null){
mapper = new JsonMapper().enableSimple();
}
return mapper;
}
/**
* 创建只输出初始值被改变的属性到Json字符串的Mapper, 最节约的存储方式,建议在内部接口中使用。
*/
public static JsonMapper nonDefaultMapper() {
if (mapper == null){
mapper = new JsonMapper(Include.NON_DEFAULT);
}
return mapper;
}
/**
* Object可以是POJO,也可以是Collection或数组。
* 如果对象为Null, 返回"null".
* 如果集合为空集合, 返回"[]".
*/
public String toJson(Object object) {
try {
return this.writeValueAsString(object);
} catch (IOException e) {
logger.warn("write to json string error:" + object, e);
return null;
}
}
/**
* 反序列化POJO或简单Collection如List<String>.
*
* 如果JSON字符串为Null或"null"字符串, 返回Null.
* 如果JSON字符串为"[]", 返回空集合.
*
* 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String,JavaType)
* @see #fromJson(String, JavaType)
*/
public <T> T fromJson(String jsonString, Class<T> clazz) {
if (StringUtils.isEmpty(jsonString)) {
return null;
}
try {
return this.readValue(jsonString, clazz);
} catch (IOException e) {
logger.warn("parse json string error:" + jsonString, e);
return null;
}
}
/**
* 反序列化复杂Collection如List<Bean>, 先使用函數createCollectionType构造类型,然后调用本函数.
* @see #createCollectionType(Class, Class...)
*/
@SuppressWarnings("unchecked")
public <T> T fromJson(String jsonString, JavaType javaType) {
if (StringUtils.isEmpty(jsonString)) {
return null;
}
try {
return (T) this.readValue(jsonString, javaType);
} catch (IOException e) {
logger.warn("parse json string error:" + jsonString, e);
return null;
}
}
/**
* 構造泛型的Collection Type如:
* ArrayList<MyBean>, 则调用constructCollectionType(ArrayList.class,MyBean.class)
* HashMap<String,MyBean>, 则调用(HashMap.class,String.class, MyBean.class)
*/
public JavaType createCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
return this.getTypeFactory().constructParametricType(collectionClass, elementClasses);
}
/**
* 當JSON裡只含有Bean的部分屬性時,更新一個已存在Bean,只覆蓋該部分的屬性.
*/
@SuppressWarnings("unchecked")
public <T> T update(String jsonString, T object) {
try {
return (T) this.readerForUpdating(object).readValue(jsonString);
} catch (JsonProcessingException e) {
logger.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
} catch (IOException e) {
logger.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
}
return null;
}
/**
* 輸出JSONP格式數據.
*/
public String toJsonP(String functionName, Object object) {
return toJson(new JSONPObject(functionName, object));
}
/**
* 設定是否使用Enum的toString函數來讀寫Enum,
* 為False時時使用Enum的name()函數來讀寫Enum, 默認為False.
* 注意本函數一定要在Mapper創建後, 所有的讀寫動作之前調用.
*/
public JsonMapper enableEnumUseToString() {
this.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
this.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
return this;
}
/**
* 允许单引号
* 允许不带引号的字段名称
*/
public JsonMapper enableSimple() {
this.configure(Feature.ALLOW_SINGLE_QUOTES, true);
this.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
return this;
}
/**
* 取出Mapper做进一步的设置或使用其他序列化API.
*/
public ObjectMapper getMapper() {
return this;
}
/**
* 对象转换为JSON字符串
* @param object
* @return
*/
public static String toJsonString(Object object){
return JsonMapper.getInstance().toJson(object);
}
/**
* JSON字符串转换为对象
* @param jsonString
* @param clazz
* @return
*/
public static Object fromJsonString(String jsonString, Class<?> clazz){
return JsonMapper.getInstance().fromJson(jsonString, clazz);
}
/**
* 测试
*/
public static void main(String[] args) {
List<Map<String, Object>> list = Lists.newArrayList();
Map<String, Object> map = Maps.newHashMap();
map.put("id", 1);
map.put("pId", -1);
map.put("name", "根节点");
list.add(map);
map = Maps.newHashMap();
map.put("id", 2);
map.put("pId", 1);
map.put("name", "你好");
map.put("open", true);
list.add(map);
// String json = JsonMapper.getInstance().toJson(list);
}
}
package com.loit.getway.utils;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
/**
* redicache 工具类
*
*/
@Component
@SuppressWarnings("unchecked")
public class RedisUtil {
@SuppressWarnings("rawtypes")
private static RedisTemplate redisTemplate;
@SuppressWarnings("rawtypes")
@Resource
public void setRedisTemplate(RedisTemplate redisTemplate){
//用于解决模糊匹配时无法匹配全部的key值,故把key去掉序列化
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
this.redisTemplate = redisTemplate;
}
/**
* 批量删除对应的value
*
* @param keys
*/
public static void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 批量删除key
*
* @param pattern
*/
public static void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0)
redisTemplate.delete(keys);
}
/**
* 删除对应的value
*
* @param key
*/
public static void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 判断缓存中是否有对应的value
*
* @param key
* @return
*/
public static boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 读取缓存
*
* @param key
* @return
*/
public static Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key);
return result;
}
/**
* 写入缓存
*
* @param key
* @param value
* @return
*/
public static boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 写入缓存
* 有效期
* @param key
* @param value
* @param expireTime 时效,单位:秒
* @return
*/
public static boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 新增一个有序集合
* @param key
* @param value
* @param expireTime
*/
public static void zAdd(final String key, String value, Long expireTime) {
try {
ZSetOperations<String, String> operations = redisTemplate.opsForZSet();
operations.add(key, value, expireTime);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除一个有序集合
* @param key
* @param value
*/
public static void zRemove(final String key, String value) {
try {
ZSetOperations<String, String> operations = redisTemplate.opsForZSet();
operations.remove(key, value);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据指定的score值得范围来移除成员
* @param key
* @param min
* @param max
*/
public static void removeRangeByScore(final String key, Double min, Double max) {
try {
ZSetOperations<String, String> operations = redisTemplate.opsForZSet();
operations.removeRangeByScore(key, min, max);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取有序集合的个数
* @param key
* @return
*/
public static Long zCard(final String key) {
Long count = 0L;
try {
ZSetOperations<String, String> operations = redisTemplate.opsForZSet();
count = operations.zCard(key);
} catch (Exception e) {
e.printStackTrace();
}
return count;
}
/**
* 获取有序集合,按分数值递增(从小到大)顺序排列
* @param key
* @return
*/
public static Set<String> zRange(final String key) {
// 删除过了有效期的数据
removeRangeByScore(key, 0.0, Double.valueOf(System.currentTimeMillis()));
Set<String> list = new HashSet<>();
try {
ZSetOperations<String, String> operations = redisTemplate.opsForZSet();
list = operations.range(key, 0, -1);
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论