Springboot微服务集成Feign

6 篇文章 0 订阅
2 篇文章 0 订阅

1.Feign简介

       Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单,它的使用方法就是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可插拔式的编码器和解码器。SpringCloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

Feign是spring cloud中服务消费端的调用框架,通常与ribbon,hystrix等组合使用,但是在某些项目中,由于遗留原因,整个系统并不是spring cloud项目,而使用者关注的重点仅仅是简化http调用代码的编写,如果采用httpclient或者okhttp这样相对较重的框架,对初学者来说编码量与学习曲线都会是一个挑战,而使用spring中RestTemplate,又没有配置化的解决方案,由此想到是否可以脱离spring cloud,独立使用Feign。

      Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。

2.SpringBoot整合Feign

2.1.添加依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>
        <!-- feign 中使用 hystrix -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!-- hystrix-dashboard -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>

2.2.添加客户端调用处理类


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

public class FeignClientInvocationHandler implements InvocationHandler{
	
	private final Logger log = LoggerFactory.getLogger(getClass());

	RestTemplate restTemplate;
	
	String rootUrl = "http://localhost:8098";
	
	public void setRestTemplate(RestTemplate restTemplate) {
		this.restTemplate = restTemplate;
	}
	public void setRootUrl(String rootUrl) {
		this.rootUrl = rootUrl;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
		String pathValue = requestMapping.value()[0];
		String url = rootUrl + pathValue;
		RequestMethod methodType = requestMapping.method()[0];
		Map<String, Object> paramsMap = new HashMap<String, Object>();
		Object body = null;
		int paramIndex = 0;
		for (Parameter parameter : method.getParameters()) {
			for (Annotation annotation : parameter.getAnnotations()) {
				boolean paramEmptyError = false;
				if (annotation instanceof RequestParam) {
					String paramName = ((RequestParam)annotation).value();
					RequestParam requestParam = (RequestParam) annotation;
					if (requestParam.required() && args[paramIndex] == null) {
						paramEmptyError = true;
					}
					paramsMap.put(paramName, args[paramIndex]);
					if (url.indexOf("?") == -1) {
						url += "?" + paramName + "={" + paramName + "}";
					} else {
						url += "&" + paramName + "={" + paramName + "}";
					}
				} else if (annotation instanceof RequestBody) {
					RequestBody requestBody = (RequestBody) annotation;
					body = args[paramIndex];
					if (requestBody.required() && body == null) {
						paramEmptyError = true;
					}
				} else if (annotation instanceof PathVariable) {
					PathVariable variable = (PathVariable) annotation;
					if (variable.required() && args[paramIndex] == null) {
						paramEmptyError = true;
					}
					paramsMap.put(variable.value(), args[paramIndex]);
				} else {
					throw new RuntimeException("参数配置不正确!接口【"+proxy.getClass().getInterfaces()[0].getName()+"】中方法【"+method.getName()+"】的参数【" + parameter.getName() + "】未定义注解!");
				}
				if (paramEmptyError) {
					throw new RuntimeException("参数配置不正确!接口【"+proxy.getClass().getInterfaces()[0].getName()+"】中方法【"+method.getName()+"】的参数【" + parameter.getName() + "】不能为空!");
				}
			}
			paramIndex ++;
		}
		Type returnType = method.getGenericReturnType();
		if (returnType instanceof ParameterizedType){
			HttpMethod httpMethod = HttpMethod.GET;
			HttpEntity<?> request = new HttpEntity<Object>(null);
			if (methodType == RequestMethod.POST) {
				httpMethod = HttpMethod.POST;
				if (body != null) {
					request = new HttpEntity<Object>(body, getPostHeaders(requestMapping));
				}
				else {
					log.warn(String.format("请求地址:%s 的类型为POST,但是没有获取到Body数据", url));
				}
			}
			return restTemplate.exchange(url, httpMethod, request, getReference(returnType), paramsMap).getBody();
		} else {
			if (methodType == RequestMethod.GET) {
				return restTemplate.getForObject(url, method.getReturnType(), paramsMap);
			} else if (methodType == RequestMethod.POST) {
				if (body != null) {
					HttpEntity<Object> request = new HttpEntity<Object>(body, getPostHeaders(requestMapping));
					return restTemplate.postForObject(url, request, method.getReturnType(), paramsMap);
				} else {
					return restTemplate.postForObject(url, body, method.getReturnType(), paramsMap);
				}
			} else if (methodType == RequestMethod.PUT) {
				restTemplate.put(url, body, paramsMap);
			} else if (methodType == RequestMethod.DELETE) {
				restTemplate.delete(url, paramsMap);
			}
		}
		return null;
	}

	private HttpHeaders getPostHeaders(RequestMapping requestMapping) {
		HttpHeaders headers = new HttpHeaders();
		if (requestMapping.consumes().length > 0) {
			headers.set(HttpHeaders.CONTENT_TYPE, requestMapping.consumes()[0]);
		} else {
			headers.setContentType(MediaType.APPLICATION_JSON);
		}
		return headers;
	}

	private <T> ParameterizedTypeReference<T> getReference(Type returnType) {
		return ParameterizedTypeReference.forType(returnType);
	}

}

2.3.添加配置类


import com.artfess.assembly.feign.impl.FeignClientInvocationHandler;
import com.artfess.base.feign.ApplicationFeignService;
import com.artfess.base.feign.BpmModelFeignService;
import com.artfess.base.feign.BpmRuntimeFeignService;
import com.artfess.base.feign.FormFeignService;
import com.artfess.base.feign.SystemConfigFeignService;
import com.artfess.base.feign.UCFeignService;
import com.artfess.base.jwt.JwtTokenHandler;
import com.artfess.base.util.HttpUtil;
import com.artfess.base.util.StringUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

@Configuration
public class AssemblyFeignServiceConfig {

	@Resource
	JwtTokenHandler jwtTokenHandler;
	@Value("${server.port}")
	public String applicationPort;
	
	@Value("${assembly.readtime:60001}")
	public Integer readTime ;
	
	@Value("${assembly.connecttime:60001}")
	public Integer connectTime;
	
	@Bean
	public RestTemplate restTemplate(ClientHttpRequestFactory factory){
		RestTemplate restTemplate = new RestTemplate(factory);
		restTemplate.getInterceptors().add(new ClientHttpRequestInterceptor() {
			@Override
			public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
					throws IOException {
				String token = "";
				HttpServletRequest orginRequest = HttpUtil.getRequest();
				if(orginRequest!=null) {
					request.getHeaders().add("User-Agent", orginRequest.getHeader("User-Agent"));
				}
				String proxyToken = "Bearer " + jwtTokenHandler.generateFeignToken();
				// 标记该请求是通过feign过来的
				request.getHeaders().add("Proxy-Authorization", proxyToken);
				try {
					token = HttpUtil.getRequest().getHeader("Authorization");
				} catch (Exception e) {
				}
				// 1.优先复制原请求中的token
				if (StringUtil.isNotEmpty(token)) {
					request.getHeaders().add("Authorization", token);
				}
				// 2.没有时使用通用token
				else{
					request.getHeaders().add("Authorization", proxyToken);
				}
				return execution.execute(request, body);
			}
		});
		return restTemplate;
	}

	@Bean
	public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
		SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
		factory.setReadTimeout(readTime);//单位为ms
		factory.setConnectTimeout(connectTime);//单位为ms
		return factory;
	}


	@Bean
	FeignClientInvocationHandler getFeignClientInvocationHandler(RestTemplate restTemplate) {
		FeignClientInvocationHandler handler = new FeignClientInvocationHandler();
		handler.setRestTemplate(restTemplate);
		handler.setRootUrl("http://localhost:" + applicationPort);
		return handler;
	}


	@SuppressWarnings("unchecked")
	private <T> T generateProxy(Class<T> clientInterface, InvocationHandler handler) {
		return (T) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { clientInterface },
				handler);
	}
}

3.添加一个服务

3.1.添加测试服务接口


import com.artfess.base.conf.FeignConfig;
import com.artfess.base.feign.impl.ApplicationFeignServiceFactory;
import com.artfess.base.jms.Notice;
import com.artfess.base.model.CommonResult;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.http.client.ClientProtocolException;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.io.IOException;
import java.util.List;
import java.util.Map;


@FeignClient(name = "bpm-application", fallbackFactory = ApplicationFeignServiceFactory.class, configuration = FeignConfig.class, primary = false)
public interface ApplicationFeignService {

    /**
     * 发送消息
     *
     * @param notice
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     */
    @RequestMapping(value = "/portal/jms/v1/sendNoticeToQueue", method = RequestMethod.POST)
    public CommonResult<String> sendNoticeToQueue(@RequestBody(required = true) Notice notice);

    /**
     * 发送消息到队列中
     *
     * @param model
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     */
    @RequestMapping(value = "/portal/jms/v1/sendToQueue", method = RequestMethod.POST)
    public CommonResult<String> sendToQueue(@RequestBody(required = true) ObjectNode model);

    /**
     * 获取用户已读未读消息
     *
     * @param account
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/innermsg/messageReceiver/v1/getMessBoxInfo", method = RequestMethod.GET)
    public ObjectNode getMessBoxInfo(@RequestParam(value = "account", required = true) String account);

    @RequestMapping(value = "/sys/sysLogsSettings/v1/getSysLogsSettingStatusMap", method = RequestMethod.GET)
    public Map<String, String> getSysLogsSettingStatusMap();

    @RequestMapping(value = "sys/authUser/v1/getAuthorizeIdsByUserMap", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
    public List<String> getAuthorizeIdsByUserMap(@RequestParam(value = "objType", required = true) String objType);

    @RequestMapping(value = "/portal/sysExternalUnite/v1/getToken", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
    public String getToken(@RequestParam(value = "type", required = true) String type);

    @RequestMapping(value = "/portal/sysExternalUnite/v1/getUserInfoUrl", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
    public String getUserInfoUrl(@RequestParam(value = "type", required = true) String type, @RequestParam(value = "code", required = true) String code);

}

3.2.添加接口实现类

 1.接口抽象类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;

import feign.hystrix.FallbackFactory;

@SuppressWarnings("unchecked")
public abstract class AbstractFallbackFeignServiceFactory<T> implements FallbackFactory<T>{

	@Override
	public T create(Throwable cause) {
		Class<T> serviceInterface = getFeignServiceClass();
		FallbackFeignServiceHandler handler = new FallbackFeignServiceHandler(serviceInterface.getSimpleName(), cause);
		return generateProxy(serviceInterface, handler);
	}

	public Class<T> getFeignServiceClass() {
		return (Class<T>) ReflectionKit.getSuperClassGenericType(getClass(), 0);
	}

	private T generateProxy(Class<T> serviceInterface, InvocationHandler handler) {
		return (T) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { serviceInterface },
				handler);
	}
	
}

2.接口继承类

import com.artfess.base.feign.ApplicationFeignService;
import com.artfess.base.feign.SystemConfigFeignService;
import org.springframework.stereotype.Component;

@Component
public class ApplicationFeignServiceFactory extends AbstractFallbackFeignServiceFactory<ApplicationFeignService>{

}

3.3.注册接口

在AssemblyFeignServiceConfig.java文件中添加以下代码

	@Bean
	@Primary
	ApplicationFeignService getApplicationFeignService(FeignClientInvocationHandler handler) {
		return generateProxy(ApplicationFeignService.class, handler);
	}

完整代码如下:


import com.artfess.assembly.feign.impl.FeignClientInvocationHandler;
import com.artfess.base.feign.ApplicationFeignService;
import com.artfess.base.feign.BpmModelFeignService;
import com.artfess.base.feign.BpmRuntimeFeignService;
import com.artfess.base.feign.FormFeignService;
import com.artfess.base.feign.SystemConfigFeignService;
import com.artfess.base.feign.UCFeignService;
import com.artfess.base.jwt.JwtTokenHandler;
import com.artfess.base.util.HttpUtil;
import com.artfess.base.util.StringUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

@Configuration
public class AssemblyFeignServiceConfig {

	@Resource
	JwtTokenHandler jwtTokenHandler;
	@Value("${server.port}")
	public String applicationPort;
	
	@Value("${assembly.readtime:60001}")
	public Integer readTime ;
	
	@Value("${assembly.connecttime:60001}")
	public Integer connectTime;
	
	@Bean
	public RestTemplate restTemplate(ClientHttpRequestFactory factory){
		RestTemplate restTemplate = new RestTemplate(factory);
		restTemplate.getInterceptors().add(new ClientHttpRequestInterceptor() {
			@Override
			public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
					throws IOException {
				String token = "";
				HttpServletRequest orginRequest = HttpUtil.getRequest();
				if(orginRequest!=null) {
					request.getHeaders().add("User-Agent", orginRequest.getHeader("User-Agent"));
				}
				String proxyToken = "Bearer " + jwtTokenHandler.generateFeignToken();
				// 标记该请求是通过feign过来的
				request.getHeaders().add("Proxy-Authorization", proxyToken);
				try {
					token = HttpUtil.getRequest().getHeader("Authorization");
				} catch (Exception e) {
				}
				// 1.优先复制原请求中的token
				if (StringUtil.isNotEmpty(token)) {
					request.getHeaders().add("Authorization", token);
				}
				// 2.没有时使用通用token
				else{
					request.getHeaders().add("Authorization", proxyToken);
				}
				return execution.execute(request, body);
			}
		});
		return restTemplate;
	}

	@Bean
	public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
		SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
		factory.setReadTimeout(readTime);//单位为ms
		factory.setConnectTimeout(connectTime);//单位为ms
		return factory;
	}


	@Bean
	FeignClientInvocationHandler getFeignClientInvocationHandler(RestTemplate restTemplate) {
		FeignClientInvocationHandler handler = new FeignClientInvocationHandler();
		handler.setRestTemplate(restTemplate);
		handler.setRootUrl("http://localhost:" + applicationPort);
		return handler;
	}

	@Bean
	@Primary
	ApplicationFeignService getApplicationFeignService(FeignClientInvocationHandler handler) {
		return generateProxy(ApplicationFeignService.class, handler);
	}


	@SuppressWarnings("unchecked")
	private <T> T generateProxy(Class<T> clientInterface, InvocationHandler handler) {
		return (T) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { clientInterface },
				handler);
	}
}

4.调用

4.1.注入Feign接口

在使用的Controller或者Service中注入Feign接口

    @Resource
    ApplicationFeignService applicationFeignService;

4.2.调用方法查询结果

String account = "接口参数";
ObjectNode node = ucFeignService.getMessBoxInfo(account);

4.3.结束

测试中使用到的工具类如下:

1.HttpUtil 


import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.artfess.base.conf.SaaSConfig;
import com.artfess.base.conf.SsoConfig;
import com.artfess.base.jwt.JwtTokenHandler;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;




public class HttpUtil {
	
	// \b 是单词边界(连着的两个(字母字符 与 非字母字符) 之间的逻辑上的间隔),    
    // 字符串在编译时会被转码一次,所以是 "\\b"    
    // \B 是单词内部逻辑间隔(连着的两个字母字符之间的逻辑上的间隔)    
   private static final String phoneReg = "\\b(ip(hone|od)|android|opera m(ob|in)i"    
            +"|windows (phone|ce)|blackberry"    
            +"|s(ymbian|eries60|amsung)|p(laybook|alm|rofile/midp"    
            +"|laystation portable)|nokia|fennec|htc[-_]"    
            +"|mobile|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";    

   private static final String tabletReg = "\\b(ipad|tablet|(Nexus 7)|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";    

  //移动设备正则匹配:手机端、平板  
   private static Pattern phonePat = Pattern.compile(phoneReg, Pattern.CASE_INSENSITIVE);    
   private static Pattern tabletPat = Pattern.compile(tabletReg, Pattern.CASE_INSENSITIVE);

	public final static String METHOD_GET = "GET";
	public final static String METHOD_POST = "POST";
   
	/**
	 * 获取当前请求的request对象
	 * @return
	 */
	public static HttpServletRequest getRequest() {
		RequestAttributes requestAttributes = null;
		try{
			requestAttributes = RequestContextHolder.currentRequestAttributes();
		}catch (IllegalStateException e){
			return null;
		}
		return ((ServletRequestAttributes) requestAttributes).getRequest();
	}
	
	/**
	 * 获取当前请求的参数
	 * @param name
	 * @return
	 */
	public static String getRequestParameter(String name) {
		HttpServletRequest request = getRequest();
		if(BeanUtils.isEmpty(request)) {
			return null;
		}
		return request.getParameter(name);
	}
	
	/**
	 * 从jwt中获取当前登录用户的tenantId
	 * @return
	 */
	private static String getTenantIdByAuthorization() {
		HttpServletRequest request = getRequest();
		if(BeanUtils.isEmpty(request)) {
			return null;
		}
		String requestHeader = request.getHeader("Authorization");
		if(BeanUtils.isNotEmpty(requestHeader) &&  requestHeader.startsWith("Bearer ")) {
			String authToken = requestHeader.substring(7);
			return HttpUtil.getTenantIdFromJwt(authToken);
			
		}
		return null;
	}
	
	/**
	 * 从jwt中解析tenantId
	 * @return
	 */
	private static String getTenantIdFromJwt(String jwt) {
		if(StringUtil.isEmpty(jwt)) {
			return null;
		}
		SaaSConfig saaSConfig = AppUtil.getBean(SaaSConfig.class);
		// 非SaaS模式,不从jwt中解析租户ID
		if(!saaSConfig.isEnable()) {
			return null;
		}
		JwtTokenHandler jwtTokenHandler = AppUtil.getBean(JwtTokenHandler.class);
		String tenantId = jwtTokenHandler.getTenantIdFromToken(jwt);
		if(StringUtil.isNotEmpty(tenantId)) {
			return tenantId;
		}
		return null;
	}
	
	/**
	 * <pre>
	 * 访问进入controller层时 设置enterController属性 
	 * 如果有tenantId 参数则从参数中获取
	 * 
	 * 没有进入controller层时 从jwttoken中获取用户认证的tenantId
	 * </pre>
	 * @return
	 */
	public static String getTenantId() {
		HttpServletRequest request = HttpUtil.getRequest();
		if(BeanUtils.isEmpty(request)) {
			return null;
		}
		Boolean enterController = (Boolean) request.getAttribute("enterController");
		String tenantId = null;
		// 只有request属性中有enterController信息时才进行判断,即只有请求Controller方法(且必须有@ApiOperation注解)时判断。
		if(BeanUtils.isNotEmpty(enterController) &&  enterController) {
			// 1.获取url地址后面是否有tenantId参数(临时租户ID,在某些情况下,只在访问某个接口时以指定租户身份来访问)
			// TODO 只有平台管理用户才能使用,防止租户之间水平越权
			tenantId = HttpUtil.getRequestParameter("tenantId");
			if(StringUtil.isNotEmpty(tenantId)) {
				return tenantId;
			}
			// 2.获取url地址后面是否有token参数(携带token进行单点登录时)
			String token = HttpUtil.getRequestParameter("ticket");
			SsoConfig ssoConfig = AppUtil.getBean(SsoConfig.class);
			// 只有非单点模式才提取ticket参数,对于cas、oauth2.0模式下时使用统一的单点登录认证
			if(!ssoConfig.isEnable() || (ssoConfig.MODE_JWT).equals(ssoConfig.getMode())) {
				tenantId = HttpUtil.getTenantIdFromJwt(token);
				if(StringUtil.isNotEmpty(tenantId)) {
					return tenantId;
				}
			}
		}
		// 3.获取请求头部的Authorization,从中解析出租户ID
		tenantId = HttpUtil.getTenantIdByAuthorization();
		if(StringUtil.isNotEmpty(tenantId)) {
			return tenantId;
		}
		return tenantId;
	}
	
	/**
	 * 下载文件。
	 *
	 * @param response
	 * @param fullPath
	 *            下载文件路径
	 * @param fileName
	 *            下载文件名
	 * @throws IOException
	 *             void
	 */
	public static void downLoadFile(HttpServletResponse response, String fullPath, String fileName) throws IOException {
		OutputStream outp = response.getOutputStream();
		File file = new File(fullPath);
		if (file.exists()) {
			response.setContentType("application/x-download");
			response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
			if (System.getProperty("file.encoding").equals("GBK")) {
				response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(), "ISO-8859-1"));
			} else {
				response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
			}
			try (FileInputStream in = new FileInputStream(fullPath);){
				IOUtils.copy(in, outp);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (outp != null) {
					outp.close();
					outp = null;
					response.flushBuffer();
				}
			}
		} else {
			outp.write("文件不存在!".getBytes("utf-8"));
		}
	}

	/**
	 * <pre>
	 * 压缩多个文件到一个zip下然后提供到页面下载
	 * 目前常用于导出xml
	 * 里面进行了1:写一个临时文件;2:打包;3:导出打包好的文件;4:删除临时文件
	 * </pre>
	 * 
	 * @param request
	 * @param response
	 * @param fileContentMap
	 *            :{a:a的内容,b:b的内容,...}
	 * @param zipName
	 *            :压缩包的名字
	 * @throws Exception
	 *             void
	 * @exception
	 * @since 1.0.0
	 */
	public static void downLoadFile(HttpServletRequest request, HttpServletResponse response, Map<String, String> fileContentMap, String zipName) throws Exception {
		String zipPath = (FileUtil.getIoTmpdir() + "attachFiles/tempZip/" + zipName).replace("/", File.separator);
		String folderPath = (FileUtil.getIoTmpdir() + "attachFiles/tempZip/" + zipName+"/").replace("/", File.separator);
		//建立临时文件夹,存放文件
		File folder=new File(folderPath);
		if(!folder.exists()) {
			folder.mkdirs();
		}

		for (Map.Entry<String, String> ent : fileContentMap.entrySet()) {
			String fileName = ent.getKey();
			String content = ent.getValue();

			String filePath = zipPath + File.separator + fileName;
			FileUtil.writeFile(filePath, content);
		}
		// 打包
		ZipUtil.zip(zipPath, true);
		// 导出
		HttpUtil.downLoadFile(response, zipPath + ".zip", zipName + ".zip");
		// 删除导出的文件
		FileUtil.deleteFile(zipPath + ".zip");
	}

	/**
	 * <pre>
	 * 压缩一个文件到压缩包下然后提供到页面下载
	 * 目前常用于导出xml
	 * 里面进行了1:写一个临时文件;2:打包;3:导出打包好的文件;4:删除临时文件
	 * </pre>
	 * 
	 * @param request
	 * @param response
	 * @param content  :要导出的文本
	 * @param fileName :文件名称
	 * @param zipName :压缩包名称
	 * @throws Exception
	 *             void
	 */
	public static void downLoadFile(HttpServletRequest request, HttpServletResponse response, String content, String fileName, String zipName) throws Exception {
		Map<String, String> fileContentMap = new HashMap<String, String>();
		fileContentMap.put(fileName, content);
		downLoadFile(request, response, fileContentMap, zipName);
	}



	/**
	 * 发送请求。
	 * 
	 * @param url
	 *            URL地址
	 * @param params
	 *            发送参数
	 * @param requestMethod
	 *            GET,POST
	 * @return
	 * @throws KeyManagementException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws IOException
	 */
	public static String sendHttpsRequest(String url, String params,
			String requestMethod) {
		HttpsURLConnection conn;
		String str = null;
		try {
			conn = getHttpsConnection(url);
			conn.setRequestMethod(requestMethod);

			conn.setDoInput(true);
			conn.setDoOutput(true);

			if (StringUtil.isNotEmpty(params)) {
				OutputStream outputStream = conn.getOutputStream();
				outputStream.write(params.getBytes("utf-8"));
				outputStream.close();
			}
			str = getOutPut(conn);
		} catch (KeyManagementException e) {
			throw new RuntimeException("远程服务器请求失败!"+e.getMessage(),e);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("远程服务器请求失败!"+e.getMessage(),e);
		} catch (NoSuchProviderException e) {
			throw new RuntimeException("远程服务器请求失败!"+e.getMessage(),e);
		} catch (IOException e) {
			throw new RuntimeException("远程服务器请求失败!"+e.getMessage(),e);
		}

		return str;
	}

	/**
	 * 获取https连接。
	 * @param accessUrl
	 * @return
	 * @throws KeyManagementException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws IOException
	 */
	public static HttpsURLConnection getHttpsConnection(String accessUrl)
			throws KeyManagementException, NoSuchAlgorithmException,
			NoSuchProviderException, IOException {
		URL url = new URL(accessUrl);
		HttpsURLConnection connection = (HttpsURLConnection) url
				.openConnection();

		TrustManager[] tm = { new MyX509TrustManager() };

		SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
		sslContext.init(null, tm, new java.security.SecureRandom());
		SSLSocketFactory ssf = sslContext.getSocketFactory();
		connection.setSSLSocketFactory(ssf);
		return connection;
	}


	/**
	 * 读取返回数据。
	 * 
	 * @param conn
	 * @return
	 * @throws IOException
	 */
	public static String getOutPut(HttpsURLConnection conn) throws IOException {
		InputStream inputStream = conn.getInputStream();
		InputStreamReader inputStreamReader = new InputStreamReader(
				inputStream, "utf-8");
		BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
		StringBuffer buffer = new StringBuffer();
		String str = null;
		while ((str = bufferedReader.readLine()) != null) {
			buffer.append(str);
		}
		bufferedReader.close();
		inputStreamReader.close();
		inputStream.close();
		conn.disconnect();
		return buffer.toString();
	}


	/**
	 * 根据url取得数据,支持gzip类网站
	 * @param url
	 * @param charset
	 * @return
	 * @throws ParseException
	 * @throws IOException
	 */
	public static String getContentByUrl(String url,String charset) throws  IOException{
		HttpClient httpclient = HttpClientBuilder.create().build();获取DefaultHttpClient请求
		HttpGet httpget = new HttpGet(url);
		HttpResponse response = httpclient.execute(httpget);


		if(StringUtil.isEmpty(charset)){
			String defaultCharset="iso-8859-1";
			Header contentTypeHeader=response.getFirstHeader("Content-Type");
			String contentType=contentTypeHeader.getValue().toLowerCase();
			if(contentType.indexOf("gbk")>-1 || contentType.indexOf("gb2312") >-1 || contentType.indexOf("gb18030")>-1){
				defaultCharset="gb18030";
			}
			else if(contentType.indexOf("utf-8")>-1){
				defaultCharset="utf-8";
			}
			else if(contentType.indexOf("big5")>-1){
				defaultCharset="big5";
			}
			charset=defaultCharset;
		}
		Header contentEncoding=response.getFirstHeader("Content-Encoding");
		StatusLine line=response.getStatusLine();
		if(line.getStatusCode()==200){
			HttpEntity entity = response.getEntity();
			InputStream is=null;
			if(contentEncoding!=null && contentEncoding.getValue().toLowerCase().equals("gzip")){
				is=new GZIPInputStream( entity.getContent());
			}
			else{
				is=entity.getContent();
			}
			String str=FileUtil.inputStream2String(is, charset);
			is.close();
			return str;

		}
		return "";
	}

	public static String sendData(String url,String data){
		return sendData( url, data, "utf-8");
	}

	/**
	 * 发送数据到指定的URL并读取返回结果。
	 * @param url
	 * @param data
	 * @return
	 */
	public static String sendData(String url,String data,String charset){
		URL uRL;
		URLConnection conn;

		BufferedReader bufferedReader = null;
		try {
			uRL = new URL(url);
			conn = uRL.openConnection();
			conn.setDoOutput(true);
			if(StringUtil.isNotEmpty(data)){
				OutputStream stream=conn.getOutputStream();
				stream.write(data.getBytes(charset));
				stream.flush();
				stream.close();
			}


			// Get the response
			bufferedReader = new BufferedReader(new InputStreamReader(
					conn.getInputStream()));
			StringBuffer response = new StringBuffer();
			String line;
			while ((line = bufferedReader.readLine()) != null) {
				response.append(line);
			}

			bufferedReader.close();

			return response.toString();
		}
		catch (MalformedURLException e) {
			e.printStackTrace();
			return "";
		} catch (IOException e) {
			e.printStackTrace();
			return "";
		}
	}
	
	/** 
     * 检测是否是移动设备访问 
     * 
     * @param userAgent 浏览器标识 
     * @return true:移动设备接入,false:pc端接入 
     */  
    public static boolean isMobile(HttpServletRequest request){
    	String userAgent = request.getHeader("user-agent");
        if(null == userAgent){    
            userAgent = "";    
        }   
        return phonePat.matcher(userAgent).find() || tabletPat.matcher(userAgent).find(); 
    }
}

2.SaaSConfig


import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 多租户的配置属性
 *
 * @author heyifan
 * @company 广州宏天软件股份有限公司
 * @email heyf@jee-soft.cn
 * @date 2020年4月9日
 */
@Component
@ConfigurationProperties(prefix = "system.saas")
public class SaaSConfig {
    /**
     * 是否开启多租户
     * <pre>
     * 未开启期间产生的所有数据不会记录租户ID,开启多租户后这部分数据不会归集到任何租户下。
     * </pre>
     */
    private boolean enable = false;
    /**
     * 多租户字段名
     */
    private String tenantId = "tenant_id_";

    /**
     * 忽略多租户的表名
     * <pre>
     * 数据库中物理表表名
     * </pre>
     */
    private List<String> ignoreTables = new ArrayList<>();

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public String getTenantId() {
        return tenantId;
    }

    public void setTenantId(String tenantId) {
        this.tenantId = tenantId;
    }

    public List<String> getIgnoreTables() {
        return ignoreTables;
    }

    public void setIgnoreTables(List<String> ignoreTables) {
        this.ignoreTables = ignoreTables;
    }
}

3.SsoConfig


import java.io.UnsupportedEncodingException;

import com.artfess.base.util.Base64;
import com.artfess.base.util.JsonUtil;
import com.artfess.base.util.StringUtil;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.node.ObjectNode;


/**
 * 单点登录配置文件
 *
 * @author liyg
 * @Date 2018-08-07
 */
@Component
@ConfigurationProperties(prefix = "sso")
public class SsoConfig {
    public final static String MODE_CAS = "cas";
    public final static String MODE_OAUTH = "oauth";
    //public final static String MODE_BASIC = "basic";
    public final static String MODE_JWT = "jwt";

    // 是否开启单点登录
    private boolean enable;
    // 单点登录模式
    private String mode;
    // cas配置
    private Cas cas;
    // oauth配置
    private Oauth oauth;

    static class Cas {
        // 基础地址
        private String url;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }
    }

    static class Oauth {
        // 基础地址
        private String url;
        // 登录路径
        private String loginPath;
        // 获取token的路径
        private String tokenPath;
        // 检查token的路径
        private String checkPath;
        // 客户端ID
        private String clientId;
        // 客户端秘钥
        private String secret;
        // 检查token的参数key
        private String checkPathKey;
        // 获取accesstoken的key
        private String accesstokenKey;
        // 获取用户账号时的key
        private String usernameKey;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getLoginPath() {
            return loginPath;
        }

        public void setLoginPath(String loginPath) {
            this.loginPath = loginPath;
        }

        public String getTokenPath() {
            return tokenPath;
        }

        public void setTokenPath(String tokenPath) {
            this.tokenPath = tokenPath;
        }

        public String getCheckPath() {
            return checkPath;
        }

        public void setCheckPath(String checkPath) {
            this.checkPath = checkPath;
        }

        public String getClientId() {
            return clientId;
        }

        public void setClientId(String clientId) {
            this.clientId = clientId;
        }

        public String getSecret() {
            return secret;
        }

        public void setSecret(String secret) {
            this.secret = secret;
        }

        public String getCheckPathKey() {
            return checkPathKey;
        }

        public void setCheckPathKey(String checkPathKey) {
            this.checkPathKey = checkPathKey;
        }

        public String getAccesstokenKey() {
            return accesstokenKey;
        }

        public void setAccesstokenKey(String accesstokenKey) {
            this.accesstokenKey = accesstokenKey;
        }

        public String getUsernameKey() {
            return usernameKey;
        }

        public void setUsernameKey(String usernameKey) {
            this.usernameKey = usernameKey;
        }
    }

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public String getMode() {
        return mode;
    }

    public void setMode(String mode) {
        this.mode = mode;
    }

    public Cas getCas() {
        return cas;
    }

    public void setCas(Cas cas) {
        this.cas = cas;
    }

    public Oauth getOauth() {
        return oauth;
    }

    public void setOauth(Oauth oauth) {
        this.oauth = oauth;
    }

    public String getCasUrl() {
        return cas.getUrl();
    }

    /**
     * 获取单点登录地址
     *
     * @return
     */
    public String getSsoUrl() {
        String ssoUrl = null;
        if (enable) {
            if (MODE_CAS.equals(mode)) {
                ssoUrl = cas.getUrl() + "?service=";
            } else if (MODE_OAUTH.equals(mode)) {
                String stufix = String.format("%s?response_type=code&client_id=%s&client_secret=%s&redirect_uri=", oauth.getLoginPath(), oauth.getClientId(), oauth.getSecret());
                ssoUrl = oauth.getUrl() + stufix;
            }
        }
        return ssoUrl;
    }

    /**
     * 获取单点退出地址
     *
     * @return
     */
    public String getSsoLogoutUrl() {
        String ssoLogoutUrl = null;
        if (enable) {
            if (MODE_CAS.equals(mode)) {
                ssoLogoutUrl = cas.getUrl() + "/logout?service=";
            } else if (MODE_OAUTH.equals(mode)) {
                ssoLogoutUrl = oauth.getUrl() + "/logout?redirect_uri=";
            }
        }
        return ssoLogoutUrl;
    }

    /**
     * 获取oauth请求token的地址
     *
     * @return
     */
    public String getOauthTokenUrl() {
        String url = null;
        if (enable && MODE_OAUTH.equals(mode)) {
            String stufix = String.format("%s?grant_type=authorization_code&client_id=%s&client_secret=%s", oauth.getTokenPath(), oauth.getClientId(), oauth.getSecret());
            url = oauth.getUrl() + stufix;
        }
        return url;
    }

    /**
     * 获取oauth验证token的地址
     *
     * @return
     */
    public String getOauthCheckUrl() {
        String url = null;
        if (enable && MODE_OAUTH.equals(mode)) {
            String stufix = String.format("%s?%s=", oauth.getCheckPath(), StringUtil.isNotEmpty(oauth.getCheckPathKey()) ? oauth.getCheckPathKey() : "access_token");
            url = oauth.getUrl() + stufix;
        }
        return url;
    }

    /**
     * 获取用户账号时的key
     *
     * @return
     */
    public String getOauthUsernameKey() {
        String usernameKey = "";
        if (enable && MODE_OAUTH.equals(mode)) {
            usernameKey = StringUtil.isNotEmpty(oauth.getUsernameKey()) ? oauth.getUsernameKey() : "username";
        }
        return usernameKey;
    }

    /**
     * 获取用户账号时的key
     *
     * @return
     */
    public String getOauthAccesstokenKey() {
        String accesstokenKey = "";
        if (enable && MODE_OAUTH.equals(mode)) {
            accesstokenKey = StringUtil.isNotEmpty(oauth.getAccesstokenKey()) ? oauth.getAccesstokenKey() : "access_token";
        }
        return accesstokenKey;
    }

    /**
     * 获取oauth认证时的basic头部
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    public String getOauthBasicHeader() throws UnsupportedEncodingException {
        String basicStr = oauth.getClientId() + ":" + oauth.getSecret();
        ObjectNode objectNode = JsonUtil.getMapper().createObjectNode();
        objectNode.put("Authorization", "Basic " + Base64.getBase64(basicStr));
        String json = objectNode.toString();
        return Base64.getBase64(json);
    }
}

4.JwtTokenHandler


import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import javax.annotation.Resource;

import com.artfess.base.cache.annotation.CacheEvict;
import com.artfess.base.cache.annotation.CachePut;
import com.artfess.base.cache.annotation.Cacheable;
import com.artfess.base.cache.annotation.FirstCache;
import com.artfess.base.cache.annotation.SecondaryCache;
import com.artfess.base.conf.JwtConfig;
import com.artfess.base.constants.CacheKeyConst;
import com.artfess.base.constants.SystemConstants;
import com.artfess.base.context.BaseContext;
import com.artfess.base.util.AppUtil;
import com.artfess.base.util.StringUtil;
import com.artfess.uc.api.model.IUser;
import com.artfess.uc.api.service.IUserService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;



import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Clock;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultClock;

@Component
public class JwtTokenHandler implements Serializable {
	@Resource
	JwtConfig jwtConfig;

	static final String CLAIM_KEY_USERNAME = "sub";
	static final String CLAIM_KEY_CREATED = "iat";
	private static final long serialVersionUID = -3301605591108950415L;
	private Clock clock = DefaultClock.INSTANCE;

	public String getUsernameFromToken(String token) {
		return getClaimFromToken(token, Claims::getSubject);
	}

	public Date getIssuedAtDateFromToken(String token) {
		return getClaimFromToken(token, Claims::getIssuedAt);
	}

	public Date getExpirationDateFromToken(String token) {
		return getClaimFromToken(token, Claims::getExpiration);
	}

	public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
		final Claims claims = getAllClaimsFromToken(token);
		return claimsResolver.apply(claims);
	}

	private Claims getAllClaimsFromToken(String token) {
		return Jwts.parser()
				.setSigningKey(jwtConfig.getSecret())
				.parseClaimsJws(token)
				.getBody();
	}

	private Boolean isTokenExpired(String token) {
		final Date expiration = getExpirationDateFromToken(token);
		return expiration.before(clock.now());
	}

	private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
		return (lastPasswordReset != null && created.before(lastPasswordReset));
	}

	private Boolean ignoreTokenExpiration(String token) {
		// here you specify tokens, for that the expiration is ignored
		return false;
	}

	public String generateToken(String userAccount) {
		IUserService userService = AppUtil.getBean(IUserService.class);
		IUser userByAccount = userService.getUserByAccount(userAccount);
		Assert.notNull(userByAccount, String.format("根据所传账号【%s】未查询到用户", userAccount));
		return generateToken(userByAccount);
	}
	
	public String generateToken(UserDetails userDetails) {
		Map<String, Object> claims = new HashMap<>();
		String tenantId = "";
		if(userDetails instanceof IUser ) {
			IUser iUser = (IUser) userDetails;
			tenantId = iUser.getTenantId();
		}
		claims.put("tenantId", tenantId);
		return doGenerateToken(claims, userDetails.getUsername());
	}

	private String doGenerateToken(Map<String, Object> claims, String subject) {
		final Date createdDate = clock.now();
		final Date expirationDate = calculateExpirationDate(createdDate);

		return Jwts.builder()
				.setClaims(claims)
				.setSubject(subject)
				.setIssuedAt(createdDate)
				.setExpiration(expirationDate)
				.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())
				.compact();
	}

	public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
		final Date created = getIssuedAtDateFromToken(token);
		return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
				&& (!isTokenExpired(token) || ignoreTokenExpiration(token));
	}

	public String refreshToken(String token) {
		final Date createdDate = clock.now();
		final Date expirationDate = calculateExpirationDate(createdDate);

		final Claims claims = getAllClaimsFromToken(token);
		claims.setIssuedAt(createdDate);
		claims.setExpiration(expirationDate);

		return Jwts.builder()
				.setClaims(claims)
				.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())
				.compact();
	}

	@SuppressWarnings("unused")
	public Boolean validateToken(String token, UserDetails userDetails) {
		final String username = getUsernameFromToken(token);
		final Date created = getIssuedAtDateFromToken(token);
		return (username.equals(userDetails.getUsername())
				&& !isTokenExpired(token));
	}

	private Date calculateExpirationDate(Date createdDate) {
		return new Date(createdDate.getTime() + jwtConfig.getExpiration() * 1000);
	}

	public String getTenantIdFromToken(String authToken) {
		Claims allClaimsFromToken = getAllClaimsFromToken(authToken);
		String tenantId = allClaimsFromToken.get("tenantId", String.class);
		return tenantId;
	}
	
	/**
	 * <pre>feign token </pre>
	 */
	public String generateFeignToken() {
		BaseContext baseContext = AppUtil.getBean(BaseContext.class);
		Map<String, Object> claims = new HashMap<>();
		String currentUserId = baseContext.getCurrentUserId();
		 String currentUserAccout = baseContext.getCurrentUserAccout();
		if ("-1".equals(currentUserAccout) || StringUtil.isEmpty(currentUserAccout)) {
			currentUserAccout = SystemConstants.SYSTEM_ACCOUNT;
		}
		String tenantId = baseContext.getCurrentTenantId();
		if("-1".equals(tenantId)){
			claims.put("userId",currentUserId );
		}
		claims.put("tenantId", tenantId);
		return doGenerateFeignToken(claims,currentUserAccout);
	}
	

	private String doGenerateFeignToken(Map<String, Object> claims, String subject) {
		final Date createdDate = clock.now();
		final Date expirationDate = calculateFeignExpirationDate(createdDate);

		return Jwts.builder()
				.setClaims(claims)
				.setSubject(subject)
				.setIssuedAt(createdDate)
				.setExpiration(expirationDate)
				.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())
				.compact();
	}
	
	@SuppressWarnings("unused")
	public Boolean validateFeignToken(String token) {
		final String username = getUsernameFromToken(token);
		BaseContext baseContext = AppUtil.getBean(BaseContext.class);
		final Date created = getIssuedAtDateFromToken(token);
		return (username.equals(baseContext.getCurrentUserAccout())
				&& !isTokenExpired(token));
	}
	
	/**
	 * <pre>
	 * feign 请求的token 设置有效时间默认为1天
	 * </pre>
	 * @param createdDate
	 * @return
	 */
	private Date calculateFeignExpirationDate(Date createdDate) {
		return new Date(createdDate.getTime() + jwtConfig.getExpiration() * 1000);
	}
	
	
	/**
	 * 获取缓存中的token
	 * @param userAgent
	 * @param tenantId
	 * @param account
	 * @param expireTime
	 * @return
	 */
	@Cacheable(value = CacheKeyConst.EIP_UC_USER_TOKEN, key = "#userAgent+'_'+#tenantId+'_'+#account", ignoreException = false,
			firstCache = @FirstCache(expireTime = 1800, expireTimeExp = "#expireTime", timeUnit = TimeUnit.SECONDS),
			secondaryCache = @SecondaryCache(expireTime = 1800, expireTimeExp = "#expireTime", preloadTime = 360, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
	public String getTokenFromCache(String userAgent, String tenantId, String account, int expireTime) {
		return null;
	}
	
	/**
	 * 将token缓存起来
	 * @param userAgent
	 * @param tenantId
	 * @param account
	 * @param expireTime
	 * @param token
	 * @return
	 */
	@CachePut(value = CacheKeyConst.EIP_UC_USER_TOKEN, key = "#userAgent+'_'+#tenantId+'_'+#account", ignoreException = false,
			firstCache = @FirstCache(expireTime = 1800, expireTimeExp = "#expireTime", timeUnit = TimeUnit.SECONDS),
			secondaryCache = @SecondaryCache(expireTime = 1800, expireTimeExp = "#expireTime", preloadTime = 360, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
	public String putTokenInCache(String userAgent, String tenantId, String account, int expireTime, String token) {
		return token;
	}
	
	/**
	 * 删除token
	 * <p>可实现服务端踢用户下线</p>
	 * @param userAgent
	 * @param tenantId
	 * @param account
	 */
	@CacheEvict(value = CacheKeyConst.EIP_UC_USER_TOKEN, key = "#userAgent+'_'+#tenantId+'_'+#account", ignoreException = false)
	public void removeFromCache(String userAgent, String tenantId, String account) {
	}
	
}

5.StringUtil


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import com.artfess.base.groovy.GroovyScriptEngine;
import com.artfess.base.util.string.StringPool;
import com.artfess.base.util.time.DateFormatUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.util.Assert;



/**
 * 字符串工具类
 *
 * @company 广州宏天软件股份有限公司
 * @author heyifan
 * @email heyf@jee-soft.cn
 * @date 2018年4月11日
 */
public class StringUtil {
	/**
	 * 将字符串里面的所有英文点符号转成空格(字符实体形式)
	 *
	 * @param str
	 *            待处理的字符串
	 * @return 每一个点对应一串:&nbsp;&emsp;
	 */
	public static String convertPointToSpace(String str) {
		String space = "";
		if (StringUtils.isEmpty(str))
			return space;
		String path[] = str.split("\\.");
		for (int i = 0; i < path.length - 1; i++) {
			space += "&nbsp;&emsp;";
		}
		return space;
	}

	/**
	 *  将输入流转utf-8字符串
	 * @param is 输入流
	 * @return 字符串
	 * @throws IOException
	 */
	public static String InputStreamToString(InputStream is) throws IOException {
		ByteArrayOutputStream result = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int length;
		while ((length = is.read(buffer)) != -1) {
		    result.write(buffer, 0, length);
		}
		String str = result.toString(StandardCharsets.UTF_8.name());
		result.close();
		return str;
	}

	/**
	 * 把字符串数组转成带,的字符串
	 *
	 * @param arr
	 * @return 返回字符串,格式如1,2,3
	 */
	public static String join(String[] arr) {
		return join(arr, StringPool.COMMA);
	}

	/**
	 *
	 * 把字符串数组转成带指定拆分字的字符串
	 *
	 * @param arr
	 * @param split
	 * @return String 返回字符串,格式如1,2,3
	 * @since 1.0.0
	 */
	public static String join(String[] arr, String split) {
		if (arr == null || arr.length == 0)
			return "";
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < arr.length; i++) {
			sb.append(arr[i]);
			sb.append(split);
		}
		return sb.substring(0, sb.length() - split.length());
	}
	/**
	 * 把字符串类型的集合转成带,的字符串
	 *
	 * @param strs
	 *            Collection<String> 适用于List、Set等。
	 * @return
	 */
	public static String join(Collection<String> strs) {
		return join(strs, StringPool.COMMA);
	}
	/**
	 *
	 /** 把字符串类型的集合转成指定拆分字符串的字符串
	 *
	 * @param strs
	 *            Collection<String> 适用于List、Set等。
	 * @param split
	 *            拆分字符串
	 * @return String
	 * @since 1.0.0
	 */
	public static String join(Collection<String> strs,String split) {
		if (strs == null || strs.isEmpty())
			return "";
		StringBuilder sb = new StringBuilder();
		Iterator<String> it = strs.iterator();
		while (it.hasNext()) {
			sb.append(it.next());
			sb.append(split);
		}
		return sb.substring(0, sb.length() - split.length());
	}

	/**
	 * 将人民币金额数字转成中文大写。
	 *
	 * @param amount
	 * @return
	 */
	public static String convertToChineseNumeral(double amount) {
		char[] hunit = { '拾', '佰', '仟' }; // 段内位置表示
		char[] vunit = { '万', '亿' }; // 段名表示
		char[] digit = { '零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖' }; // 数字表示
		long midVal = (long) (amount * 100); // 转化成整形
		String valStr = String.valueOf(midVal); // 转化成字符串

		String head = valStr.substring(0, valStr.length() - 2); // 取整数部分
		String rail = valStr.substring(valStr.length() - 2); // 取小数部分

		String prefix = ""; // 整数部分转化的结果
		String suffix = ""; // 小数部分转化的结果
		// 处理小数点后面的数
		if (rail.equals("00")) { // 如果小数部分为0
			suffix = "整";
		} else {
			suffix = digit[rail.charAt(0) - '0'] + "角"
					+ digit[rail.charAt(1) - '0'] + "分"; // 否则把角分转化出来
		}
		// 处理小数点前面的数
		char[] chDig = head.toCharArray(); // 把整数部分转化成字符数组
		char zero = '0'; // 标志'0'表示出现过0
		byte zeroSerNum = 0; // 连续出现0的次数
		for (int i = 0; i < chDig.length; i++) { // 循环处理每个数字
			int idx = (chDig.length - i - 1) % 4; // 取段内位置
			int vidx = (chDig.length - i - 1) / 4; // 取段位置
			if (chDig[i] == '0') { // 如果当前字符是0
				zeroSerNum++; // 连续0次数递增
				if (zero == '0') { // 标志
					zero = digit[0];
				} else if (idx == 0 && vidx > 0 && zeroSerNum < 4) {
					prefix += vunit[vidx - 1];
					zero = '0';
				}
				continue;
			}
			zeroSerNum = 0; // 连续0次数清零
			if (zero != '0') { // 如果标志不为0,则加上,例如万,亿什么的
				prefix += zero;
				zero = '0';
			}
			prefix += digit[chDig[i] - '0']; // 转化该数字表示
			if (idx > 0) {
				prefix += hunit[idx - 1];
			}
			if (idx == 0 && vidx > 0) {
				prefix += vunit[vidx - 1]; // 段结束位置应该加上段名如万,亿
			}
		}

		if (prefix.length() > 0) {
			prefix += '圆'; // 如果整数部分存在,则有圆的字样
		}
		return prefix + suffix; // 返回正确表示
	}
	/**
	 * 去除字符串里的html标签
	 *
	 * @param content
	 *            待转换的字符串
	 * @return
	 */
	public static String stripHtml(String content) { 
	    // <p>段落替换为换行 
	    content = content.replaceAll("<p .*?>", "\r\n"); 
	    // <br><br/>替换为换行 
	    content = content.replaceAll("<br\\s*/?>", "\r\n"); 
	    // 去掉其它的<>之间的东西 
	    content = content.replaceAll("\\<.*?>", ""); 
	    // 去掉空格 
	    content = content.replaceAll(" ", ""); 
	    return content;   
	}
	/**
	 * 将传入字符串中的Html字符实体(character entities)转换成Html预留字符
	 *
	 * @param content
	 *            待转换的字符串
	 * @return
	 */
	public static String convertCharEntityToHtml(String content) {
		content = content.replace("&apos;", "'").replace("&quot;", "\"")
				.replace("&gt;", ">").replace("&lt;", "<")
				.replace("&amp;", "&");

		int start = 0;
		int end = 0;
		final StringBuilder buffer = new StringBuilder();

		while (start > -1) {
			int system = 10;// 进制
			if (start == 0) {
				int t = content.indexOf("&#");
				if (start != t) {
					start = t;
				}
				if (start > 0) {
					buffer.append(content.substring(0, start));
				}
			}
			end = content.indexOf(";", start + 2);
			String charStr = "";
			if (end != -1) {
				charStr = content.substring(start + 2, end);
				// 判断进制
				char s = charStr.charAt(0);
				if (s == 'x' || s == 'X') {
					system = 16;
					charStr = charStr.substring(1);
				}
			}
			// 转换
			try {
				char letter = (char) Integer.parseInt(charStr, system);
				buffer.append(new Character(letter).toString());
			} catch (NumberFormatException e) {
				e.printStackTrace();
			}

			// 处理当前unicode字符到下一个unicode字符之间的非unicode字符
			start = content.indexOf("&#", end);
			if (start - end > 1) {
				buffer.append(content.substring(end + 1, start));
			}

			// 处理最后面的非unicode字符
			if (start == -1) {
				int length = content.length();
				if (end + 1 != length) {
					buffer.append(content.substring(end + 1, length));
				}
			}
		}
		return buffer.toString();
	}

	/**
	 * 将传入的字符串的Html预留字符转换成Html字符实体(character entities)
	 *
	 * @param content
	 *            待转换的字符串(一般为Html代码)
	 * @return
	 */
	public static String convertHtmlToCharEntity(String content) {
		final StringBuilder sb = new StringBuilder();
		for (int i = 0; i < content.length(); i++) {
			char c = content.charAt(i);

			switch (c) {
			case 0x0A:
				sb.append(c);
				break;

			case '<':
				sb.append("&lt;");
				break;

			case '>':
				sb.append("&gt;");
				break;

			case '&':
				sb.append("&amp;");
				break;

			case '\'':
				sb.append("&apos;");
				break;

			case '"':
				sb.append("&quot;");
				break;

			default:
				if ((c < ' ') || (c > 0x7E)) {
					sb.append("&#x");
					sb.append(Integer.toString(c, 16));
					sb.append(';');
				} else {
					sb.append(c);
				}
			}
		}
		return sb.toString();
	}

	/**
	 * 格式化带参数的字符串,如 /param/detail.ht?a=${0}&b=${1}
	 * 注意字符串的参数从0下标开始,字符串的参数数量和args数组的数量要一致。
	 *
	 * @param message
	 * @param args
	 * @return
	 */
	public static String format(String message, Object... args) {
		for (int i = 0; i < args.length; i++) {
			message = message.replace("${" + i + "}", args[i].toString());
		}
		return message;
	}

	/**
	 * 格式化如下字符串 http://www.bac.com?a=${a}&b=${b}
	 *
	 * @param message
	 * @param params
	 */
	public static String format(String message, Map<String, Object> params) {
		String result = message;
		if (params == null ||  params.isEmpty())
			return result;
		Iterator<String> keyIts = params.keySet().iterator();
		while (keyIts.hasNext()) {
			String key = keyIts.next();
			Object value = params.get(key);
			if (value != null) {
				result = result.replace("${" + key + "}", value.toString());
			}
		}
		return result;
	}

	/**
	 * 简单的字符串格式化,性能较好。支持不多于10个占位符,从%1开始计算,数目可变。参数类型可以是字符串、Integer、Object,
	 * 甚至int等基本类型
	 * 、以及null,但只是简单的调用toString(),较复杂的情况用String.format()。每个参数可以在表达式出现多次。
	 *
	 * @param msgWithFormat
	 * @param autoQuote
	 *            是否加中括号将结果括起来
	 * @param args
	 * @return
	 */
	public static StringBuilder format(CharSequence msgWithFormat,
			boolean autoQuote, Object... args) {
		int argsLen = args.length;
		boolean markFound = false;

		StringBuilder sb = new StringBuilder(msgWithFormat);

		if (argsLen > 0) {
			for (int i = 0; i < argsLen; i++) {
				String flag = "%" + (i + 1);
				int idx = sb.indexOf(flag);
				// 支持多次出现、替换的代码
				while (idx >= 0) {
					markFound = true;
					sb.replace(idx, idx + 2, toString(args[i], autoQuote));
					idx = sb.indexOf(flag);
				}
			}

			if (args[argsLen - 1] instanceof Throwable) {
				StringWriter sw = new StringWriter();
				((Throwable) args[argsLen - 1])
						.printStackTrace(new PrintWriter(sw));
				sb.append("\n").append(sw.toString());
			} else if (argsLen == 1 && !markFound) {
				sb.append(args[argsLen - 1].toString());
			}
		}
		return sb;
	}

	/**
	 * 判断指定的内容是否存在
	 *
	 * @param content
	 *            内容
	 * @param beginStr
	 *            开始内容
	 * @param endStr
	 *            结束内容
	 * @return
	 */
	public static boolean isExist(String content, String beginStr, String endStr) {
		final boolean isExist = true;
		// 转成小写
		String lowContent = content.toLowerCase();
		String lowBeginStr = beginStr.toLowerCase();
		String lowEndStr = endStr.toLowerCase();

		int beginIndex = lowContent.indexOf(lowBeginStr);
		int endIndex = lowContent.indexOf(lowEndStr);
		if (beginIndex != -1 && endIndex != -1 && beginIndex < endIndex) {
			return isExist;
		}
		return !isExist;
	}

	/**
	 * 对字符串去掉前面的指定字符
	 *
	 * @param content
	 *            待处理的字符串
	 * @param prefix
	 *            要去掉前面的指定字符串
	 * @return
	 */
	public static String trimPrefix(String content, String prefix) {
		String resultStr = content;
		while (resultStr.startsWith(prefix)) {
			resultStr = resultStr.substring(prefix.length());
		}
		return resultStr;
	}

	/**
	 * 对字符串去掉前面的指定字符
	 *
	 * @param content
	 *            待处理的字符串
	 * @param suffix
	 *            要去掉后面的指定字符串
	 * @return
	 */
	public static String trimSuffix(String content, String suffix) {
		String resultStr = content;
		while (resultStr.endsWith(suffix)) {
			resultStr = resultStr.substring(0,
					resultStr.length() - suffix.length());
		}
		return resultStr;
	}

	/**
	 * 对字符串的前后均去掉前面的指定字符
	 *
	 * @param content
	 * @param trimStr
	 * @return
	 */
	public static String trim(String content, String trimStr) {
		return trimSuffix(trimPrefix(content, trimStr), trimStr);
	}

	/**
	 * 把字符串的第一个字母转为大写
	 *
	 * @param str 字符串
	 * @return
	 */
	public static String upperFirst(String str) {
		return toFirst(str, true);
	}

	/**
	 * 判断字符串非空
	 *
	 * @param str
	 * @return
	 */
	public static boolean isNotEmpty(String str) {
		return !isEmpty(str);
	}

	/**
	 * 判断字符串是否为空
	 *
	 * @param str
	 * @return
	 */
	public static boolean isEmpty(String str) {

		if (str == null)
			return true;
		if (str.trim().equals(""))
			return true;
		if("null".equals(str)) {
			return true;
		}
		return false;
	}

	/**
	 * 判断Long是否为空
	 *
	 * @param value
	 * @return
	 */
	public static boolean isEmpty(Long value) {
		if (value == null)
			return true;
		if (value.longValue()==0)
			return true;
		return false;
	}


	/**
	 * 为空判断,0做空处理。
	 * <pre>
	 *这里判断:
	 *1.字符串为NULL
	 *2.字符串为空串。
	 *3.字符串为0。
	 * </pre>
	 * @param tmp
	 * @return
	 * boolean
	 */
	public static boolean isZeroEmpty(String tmp){
		boolean isEmpty=StringUtil.isEmpty(tmp);
		if(isEmpty) return true;
		return "0".equals(tmp);
	}

	/**
	 * 非空判断。
	 * @param tmp
	 * @return
	 * boolean
	 */
	public static boolean isNotZeroEmpty(String tmp){
		return !isZeroEmpty(tmp);
	}

	/**
	 * 把字符串的第一个字母转为小写
	 *
	 * @param str
	 * @return
	 */
	public static String lowerFirst(String str) {
		return toFirst(str, false);
	}

	/**
	 * 把字符串的第一个字母转为大写或者小写
	 *
	 * @param str
	 *            字符串
	 * @param isUpper
	 *            是否大写
	 * @return
	 */
	public static String toFirst(String str, boolean isUpper) {
		if (StringUtils.isEmpty(str))
			return "";
		char first = str.charAt(0);
		String firstChar = new String(new char[] { first });
		firstChar = isUpper ? firstChar.toUpperCase() : firstChar.toLowerCase();
		return firstChar + str.substring(1);
	}

	/**
	 * 将content中所有{...}的替换为replace参数内容
	 *
	 * @param content
	 *            待替换的字符串
	 * @param replace
	 *            替换的字符串
	 * @return 替换后的字符串,如content=abc{aa}{bb} ; replace ="ff",结果就是abcffff
	 */
	public static String replaceVariable(String content, String replace) {
		return replaceVariable(content, replace, "\\{(.*?)\\}");
	}

	/**
	 * 将content中所有符合regular正则表达式的内容替换为replace参数内容
	 *
	 * @param content
	 *            待替换的字符串
	 * @param replace
	 *            替换的字符串
	 * @param regular
	 *            正则表达式
	 * @return 替换后的字符串。 如content=abc{aa}{bb} ; replace
	 *         ="ff",regular="\\{(.*?)\\}";结果就是abcffff
	 */
	public static String replaceVariable(String content, String replace,
			String regular) {
		Pattern regex = Pattern.compile(regular);
		String result = content;
		Matcher regexMatcher = regex.matcher(result);
		while (regexMatcher.find()) {
			String toReplace = regexMatcher.group(0);
			result = result.replace(toReplace, replace);
			regexMatcher = regex.matcher(result);
		}
		return result;
	}

	/**
	 * 对传入的字符串(content)进行变量值替换(map) 采用默认的正则表达式:\\{(.*?)\\}
	 *
	 * @param content
	 *            要处理的字符串
	 * @param map
	 *            替换参数和值的集合
	 * @return 替换后的字符串
	 * @throws Exception
	 */
	public static String replaceVariableMap(String content,
			Map<String, Object> map) throws Exception {
		return replaceVariableMap(content, map, "\\{(.*?)\\}");
	}

	/**
	 *
	 * @param template
	 *            要处理的字符串
	 * @param map
	 *            替换参数和值的集合
	 * @param regular
	 *            正则表达式
	 * @return 替换后的字符串
	 * @throws Exception
	 *             如果template的某个
	 */
	public static String replaceVariableMap(String template,
			Map<String, Object> map, String regular) throws Exception {
		Pattern regex = Pattern.compile(regular);
		Matcher regexMatcher = regex.matcher(template);
		while (regexMatcher.find()) {
			String key = regexMatcher.group(1);
			String toReplace = regexMatcher.group(0);
			String value = (String) map.get(key);
			if (value != null) {
				template = template.replace(toReplace, value);
			} else {
				throw new Exception("没有找到[" + key + "]对应的变量值,请检查表变量配置!");
			}
		}

		return template;
	}

	/**
	 * 根据默认的特殊字符正则表达式去除特殊字符
	 *
	 * @param str
	 * @return
	 */
	public static String removeSpecial(String str)
			throws PatternSyntaxException {
		return removeByRegEx(str, StringPool.SPECIAL_REG_EX);
	}

	/**
	 * 根据传入的字符串(参数str),通过正则表达式(参数regEx),去掉该表达式匹配的字符。
	 *
	 * @param str
	 *            待处理的字符串
	 * @param regEx
	 *            正则表达式
	 * @return
	 * @throws PatternSyntaxException
	 */
	public static String removeByRegEx(String str, String regEx)
			throws PatternSyntaxException {
		// 清除掉所有特殊字符
		Pattern p = Pattern.compile(StringPool.SPECIAL_REG_EX);
		Matcher m = p.matcher(str);
		return m.replaceAll("").trim();
	}

	/**
	 * String转Byte数组
	 *
	 * @param str
	 * @return
	 */
	public static byte[] stringToBytes(String str) {
		byte digest[] = new byte[str.length() / 2];
		for (int i = 0; i < digest.length; i++) {
			String byteString = str.substring(2 * i, 2 * i + 2);
			int byteValue = Integer.parseInt(byteString, 16);
			digest[i] = (byte) byteValue;
		}

		return digest;
	}

	/**
	 * Byte数组转String
	 *
	 * @param b
	 * @return
	 */
	public static String bytesToString(byte b[]) {
		StringBuffer hexString = new StringBuffer();
		for (int i = 0; i < b.length; i++) {
			String plainText = Integer.toHexString(0xff & b[i]);
			if (plainText.length() < 2) {
				plainText = "0" + plainText;
			}
			hexString.append(plainText);
		}
		return hexString.toString();
	}

	/**
	 * 将对象转成格式化的字符串输出
	 *
	 * @param obj
	 *            任意对象
	 * @param autoQuote
	 *            是否加中括号将结果括起来
	 * @return
	 */
	public static String toString(Object obj, boolean autoQuote) {
		StringBuilder sb = new StringBuilder();
		if (obj == null) {
			sb.append("NULL");
		} else {
			if (obj instanceof Object[]) {
				for (int i = 0; i < ((Object[]) obj).length; i++) {
					sb.append(((Object[]) obj)[i]).append(", ");
				}
				if (sb.length() > 0) {
					sb.delete(sb.length() - 2, sb.length());
				}
			} else {
				sb.append(obj.toString());
			}
		}
		if (autoQuote
				&& sb.length() > 0
				&& !((sb.charAt(0) == '[' && sb.charAt(sb.length() - 1) == ']') || (sb
						.charAt(0) == '{' && sb.charAt(sb.length() - 1) == '}'))) {
			sb.insert(0, "[").append("]");
		}
		return sb.toString();
	}

	/**
	 * 字符串 编码转换
	 *
	 * @param str
	 *            字符串
	 * @param from
	 *            原來的編碼
	 * @param to
	 *            轉換后的編碼
	 * @return
	 */
	public static String encodingString(String str, String from, String to) {
		String result = str;
		try {
			result = new String(str.getBytes(from), to);
		} catch (Exception e) {
			result = str;
		}
		return result;
	}

	 /**
     *
     * <p>获取最后被截取后后面的字符串</p>
     *
     * <pre>
     * StringUtils.substringAfterLast(null, *)        = null
     * StringUtils.substringAfterLast("", *)          = ""
     * StringUtils.substringAfterLast(*, "")          = ""
     * StringUtils.substringAfterLast(*, null)        = ""
     * StringUtils.substringAfterLast("abc", "a")     = "bc"
     * StringUtils.substringAfterLast("abcba", "b")   = "a"
     * StringUtils.substringAfterLast("abc", "c")     = ""
     * StringUtils.substringAfterLast("a", "a")       = ""
     * StringUtils.substringAfterLast("a", "z")       = ""
     * StringUtils.substringAfterLast("a.b.c.d", ".") = "d"
     * </pre>
     *
     */
    public static String substringAfterLast(String str, String separator) {
    	return StringUtils.substringAfterLast(
    			str, separator);
    }

    /**
     * <p>获取最后被截取后前面的字符串</p>
     *
     * <pre>
     * StringUtils.substringBeforeLast(null, *)       = null
     * StringUtils.substringBeforeLast("", *)         = ""
     * StringUtils.substringBeforeLast("abcba", "b")  = "abc"
     * StringUtils.substringBeforeLast("abc", "c")    = "ab"
     * StringUtils.substringBeforeLast("a", "a")      = ""
     * StringUtils.substringBeforeLast("a", "z")      = "a"
     * StringUtils.substringBeforeLast("a", null)     = "a"
     * StringUtils.substringBeforeLast("a", "")       = "a"
     * StringUtils.substringAfterLast("a.b.c.d", ".") = "a.b.c"
     * </pre>
     *
     */
    public static String substringBeforeLast(String str, String separator){
    	return StringUtils.substringBeforeLast(
    			str, separator);
    }

	/**
	 * 删除后面指定的字符
	 *
	 * @param toTrim
	 * @param trimStr
	 * @return
	 */
	public static String trimSufffix(String toTrim, String trimStr) {
		while (toTrim.endsWith(trimStr)) {
			toTrim = toTrim.substring(0, toTrim.length() - trimStr.length());
		}
		return toTrim;
	}

	/**
	 * 将数据库字段名转为DataGrid字段名
	 * isIgnoreFirst:是否忽略第一个字段不转大写
	 * @return
	 */
	public static  String convertDbFieldToField(String dbField)
	{
		return convertDbFieldToField(dbField,"_",true);
	}

	/**
	 * 将数据库字段名转为DataGrid字段名,如 sys_data_ 转为sysData
	 * symbol:间隔符号
	 * isIgnoreFirst:是否忽略第一个单词的首字母转大写
	 * @return
	 */
	public static  String convertDbFieldToField(String dbField,String symbol,boolean isIgnoreFirst)
	{
	   String result="";
	   if(dbField.startsWith(symbol))dbField=dbField.substring(1);
	   if(dbField.endsWith(symbol))dbField=dbField.substring(0,dbField.length()-1);
	   String[] arr=dbField.split(symbol);
	   for (int i=0;i<arr.length;i++)
	   {
		   String str=arr[i];
		   if(isIgnoreFirst&&i!=0)
		   {
		      char oldChar = str.charAt(0);
		      char newChar = (oldChar + "").toUpperCase().charAt(0);
		      str = newChar+str.substring(1);
		   }
		   result+=str;
	   }
	   return result;
	}

	/**
     * 把String 转换成指定的对象
     * @param obj
     * @param type
     * @return
     */
    public static Object parserObject(Object obj, String type) {
		if (obj==null)
			return null;
		Object val = obj;
		try {
			String str  =obj.toString();
			if (type.equalsIgnoreCase("string")) {
				val = str;
			} else if (type.equalsIgnoreCase("int")) {
				val = Integer.parseInt(str);
			} else if (type.equalsIgnoreCase("float")) {
				val = Float.parseFloat(str);
			} else if (type.equalsIgnoreCase("double")) {
				val = Double.parseDouble(str);
			} else if (type.equalsIgnoreCase("byte")) {
				val = Byte.parseByte(str);
			} else if (type.equalsIgnoreCase("short")) {
				val = Short.parseShort(str);
			} else if (type.equalsIgnoreCase("long")) {
				val = Long.parseLong(str);
			} else if (type.equalsIgnoreCase("boolean")) {
				if (StringUtils.isNumeric(str))
					val = (Integer.parseInt(str) == 1 ? true : false);
				val = Boolean.parseBoolean(str);
			} else if (type.equalsIgnoreCase("date")) {
				val = DateFormatUtil.parse(str);
			} else {
				val = str;
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
		return val;
	}

	/**
     * 把String 转换成指定的对象
     * @param objStr
     * @param type
     * @return
     */
    public static Object parserObject(String objStr, Class<?> type) {
		if (StringUtil.isEmpty(objStr))
			return null;
		try {
			switch (type.getSimpleName()) {
			case "String":
			case "Boolean":
			case "Byte":
			case "Short":
			case "Integer":
			case "Long":
			case "Float":
			case "Double":
				return type.getConstructor(String.class).newInstance(objStr);
			case "boolean":
			case "byte":
			case "short":
			case "int":
			case "long":
			case "float":
			case "double":
				return parserObject(objStr, type.getSimpleName());
			case "Date":
				return DateUtils.parseDate(objStr, new String[] {"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "HH:mm:ss"});
			case "LocalDate":
				return DateFormatUtil.dateParse(objStr, "yyyy-MM-dd");
			case "LocalDateTime":
				return DateFormatUtil.parseDateTime(objStr);
			default:
				break;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}


	/**
	 * 根据一串逗号分隔的字符串取得字符串形数组
	 * @param str
	 * @author zhaoxy
	 * @return
	 */
	public static String[] getStringAryByStr(String str) {
		if (StringUtil.isEmpty(str))
			return null;
		String[] aryId = str.split(",");
		String[] lAryId = new String[aryId.length];
		for (int i = 0; i < aryId.length; i++) {
			lAryId[i] = (aryId[i]);
		}
		return lAryId;
	}

	/**
	 * 根据一串逗号分隔的长整型字符串取得长整形数组
	 *
	 * @param str 字符串参数
	 * @return    返回长整型数组
	 */
	public static Long[] getLongAryByStr(String str) {
		if (StringUtil.isEmpty(str))
			return null;
		str = str.replace("[", "");
		str = str.replace("]", "");
		String[] aryId = str.split(",");
		Long[] lAryId = new Long[aryId.length];
		for (int i = 0; i < aryId.length; i++) {
			lAryId[i] = Long.parseLong(aryId[i]);
		}
		return lAryId;
	}


	/**
	 * string转map
	 * @param param
	 * @return
	 */
	public static Object getMapValue(String param) {
        Map<String, Object> map = new HashMap<>();
        String str = "";
        String key = "";
        Object value = "";
        char[] charList = param.toCharArray();
        boolean valueBegin = false;
        for (int i = 0; i < charList.length; i++) {
            char c = charList[i];
            if (c == '{') {
                if (valueBegin == true) {
                    value = getMapValue(param.substring(i, param.length()));
                    i = param.indexOf('}', i) + 1;
                    map.put(key, value);
                }
            } else if (c == '=') {
                valueBegin = true;
                key = str;
                str = "";
            } else if (c == ',') {
                valueBegin = false;
                value = str;
                str = "";
                map.put(key, value);
            } else if (c == '}') {
                if (str != "") {
                    value = str;
                }
                map.put(key, value);
                return map;
            } else if (c != ' ') {
                str += c;
            }
        }
        return map;
    }

	/**
	 * 值为List转为以逗号隔开并且以单引号括起来的字符串
	 *
	 * @param listMap
	 * @return
	 */
	public static Map<String, String> getMapStringByMapList(Map<String, Set<String>> listMap){
		Map<String, String> map = new HashMap<String, String>();
		for (String key : listMap.keySet()){
			Set<String> list = listMap.get(key);
			if(list==null)continue;
			String valueString = convertListToSingleQuotesString(list);
			if(StringUtil.isNotEmpty(valueString)){
				map.put(key, valueString);
			}
		}
		return map;
	}

	/**
	 * List转成以单引号括起来字符串
	 *
	 * @param set
	 * @return
	 */
	public static String convertListToSingleQuotesString(Set<String> set){
		if(set==null)return "";
		String ids = "";
		for (String value : set){
			ids = ids + "\'" + value + "\',";
		}
		ids = ids.equals("") ? "" : ids.substring(0, ids.length() - 1);
		return ids;
	}

	public static boolean equals(String st1, String st2) {
		if (BeanUtils.isEmpty(st1) && BeanUtils.isEmpty(st2)) {
			return true;
		}
		if (BeanUtils.isNotEmpty(st1) && BeanUtils.isNotEmpty(st1)) {
			return st1.equals(st2);
		}
		return false;
	}
	public static boolean LargeThen(String st1, String st2) {
		if (BeanUtils.isNotEmpty(st1) && BeanUtils.isNotEmpty(st1)) {
			return st1.compareTo(st2) > 0;
		}
		return false;
	}
	public static boolean littleThen(String st1, String st2) {
		if (BeanUtils.isNotEmpty(st1) && BeanUtils.isNotEmpty(st1)) {
			return st1.compareTo(st2) < 0;
		}
		return false;
	}

	public static boolean contains(String st1, String st2) {
		if (BeanUtils.isNotEmpty(st1) && BeanUtils.isNotEmpty(st1)) {
			return st1.contains(st2);
		}
		return false;
	}
	public static boolean isStringBelongTo(String target, String boundary) {
		if (BeanUtils.isEmpty(target) || BeanUtils.isEmpty(boundary)) {
			return false;
		}
		String[] boundaryArr = boundary.split(",");
		if (boundaryArr.length <= 0) {
			return false;
		}
		List<String> list = Arrays.asList(boundaryArr);
		return list.contains(target);
	}
	
	/**
     * <pre>
     * 对字符串进行脱敏处理
     *  System.out.println(StringUtil.wordMask("123456789", 0, 1, "*")); *23456789
	 *	System.out.println(StringUtil.wordMask("123456789", 1, 1, "*")); *23456789
	 *	System.out.println(StringUtil.wordMask("123456789", 0, 2, "*")); **3456789
	 *	System.out.println(StringUtil.wordMask("123456789", 1, 2, "*")); **3456789
     * </pre>
     * 
     *
     * @param word 被脱敏的字符
     * @param startLength 被保留的开始长度 前余n位
     * @param endLength 被保留的结束长度 后余n位
     * @param pad 填充字符
     * */
    public static String wordMask(String word,int startLength ,int endLength,String pad)    {
    	Assert.isTrue(startLength>=0, "开始位置必须大于等于0");
    	Assert.isTrue(endLength>=0, "开始位置必须大于等于0");
    	Assert.isTrue(startLength<=endLength, "开始位置必须小于等于结束位置");
    	if(isEmpty(word)) {
    		return word;
    	}
    	if(endLength>word.length()) {
    		endLength = word.length();
    	}
    	if(startLength>endLength) {
    		return word;
    	}
    	if(startLength>0) {
    		startLength = startLength -1;
    	}
        String startStr = word.substring(0, startLength);
        
        String endStr = word.substring(endLength, word.length());
 
        return startStr + StringUtils.leftPad("", endLength - startLength, pad) + endStr;
 
    }
	/**
	 * 检验手机号
	 * @param mobile
	 * @return
	 */
	public static boolean isMobile(String mobile) {
		mobile = String.valueOf(mobile).trim();
		Pattern regex = Pattern.compile("^((1[3-9]{1}))\\d{9}$");
		Matcher matcher = regex.matcher(mobile);
		boolean isMatched = matcher.matches();
		return isMatched;
	}

	/**
	 * 检查email是否是邮箱格式,返回true表示是,反之为否
	 * @param email
	 * @return
	 */
	public static boolean isEmail(String email) {
		email = String.valueOf(email).trim();
		Pattern regex = Pattern.compile("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*");
		Matcher matcher = regex.matcher(email);
		boolean isMatched = matcher.matches();
		return isMatched;
	}
	
	/**
	 * 格式化double为字符串,不丢失精度
	 * @param value
	 * @return
	 */
	public static String format(double value) {
		String dstr = String.valueOf(value);
		String earr[] = dstr.split("E");
		if (earr.length == 2) {
			int e = Integer.valueOf(earr[1]);
			String dotArr[] = earr[0].split("[.]");
			int precision = dotArr[1].length() - e; 
			dstr = new BigDecimal(value).setScale(precision, RoundingMode.HALF_EVEN).toPlainString();
		} else if (value % 1 == 0){
			dstr =  String.valueOf(Double.valueOf(value).intValue());
		}
		return dstr;
	}
    
	/**
	 * 判断字符串中是否包含emoji表情
	 * @param content
	 * @return
	 */
	public static boolean hasEmoji(String content){
		Pattern pattern = Pattern.compile("(?:[\uD83C\uDF00-\uD83D\uDDFF]|[\uD83E\uDD00-\uD83E\uDDFF]|[\uD83D\uDE00-\uD83D\uDE4F]|[\uD83D\uDE80-\uD83D\uDEFF]|[\u2600-\u26FF]\uFE0F?|[\u2700-\u27BF]\uFE0F?|\u24C2\uFE0F?|[\uD83C\uDDE6-\uD83C\uDDFF]{1,2}|[\uD83C\uDD70\uD83C\uDD71\uD83C\uDD7E\uD83C\uDD7F\uD83C\uDD8E\uD83C\uDD91-\uD83C\uDD9A]\uFE0F?|[\u0023\u002A\u0030-\u0039]\uFE0F?\u20E3|[\u2194-\u2199\u21A9-\u21AA]\uFE0F?|[\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55]\uFE0F?|[\u2934\u2935]\uFE0F?|[\u3030\u303D]\uFE0F?|[\u3297\u3299]\uFE0F?|[\uD83C\uDE01\uD83C\uDE02\uD83C\uDE1A\uD83C\uDE2F\uD83C\uDE32-\uD83C\uDE3A\uD83C\uDE50\uD83C\uDE51]\uFE0F?|[\u203C\u2049]\uFE0F?|[\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE]\uFE0F?|[\u00A9\u00AE]\uFE0F?|[\u2122\u2139]\uFE0F?|\uD83C\uDC04\uFE0F?|\uD83C\uDCCF\uFE0F?|[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?)");
		Matcher matcher = pattern.matcher(content);
		return matcher.find();
	}
    
    public static void main(String[] args) {
		System.out.println(StringUtil.wordMask("123456789", 0, 1, "*"));
		System.out.println(StringUtil.wordMask("123456789", 9, 15, "*"));
		
		System.out.println(StringUtil.wordMask("123456789", 0, 2, "*"));
		System.out.println(StringUtil.wordMask("123456789", 1, 2, "*"));
	}
    
    /**
	 * 根据规则模版获取标题。
	 * 
	 * <pre>
	 * 示例:
	 * String rule="{depart:某个部门}申请{时间}公司{活动}";
	 * Map<String,String> map=new HashMap<String,String>();
	 * map.put("depart","测试部");
	 * map.put("时间","周末");
	 * map.put("活动","踏青");
	 * String subject=BpmUtil.getTitleByRule(rule,map);
	 * 返回:
	 * 测试部申请周末公司踏青
	 * </pre>
	 * 
	 * @param titleRule
	 *            主题规则
	 * @param map
	 *            变量map,用于替换规则中的变量。
	 * @return String
	 */
	public static String getStrByRule(String titleRule, Map<String, Object> map) {
		if (StringUtils.isEmpty(titleRule))
			return "";
		Pattern regex = Pattern.compile("\\{(.*?)\\}", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
		Matcher matcher = regex.matcher(titleRule);
		while (matcher.find()) {
			String tag = matcher.group(0);
			String rule = matcher.group(1);
			String[] aryRule = rule.split(":");
			String name = "";
			if (aryRule.length == 1) {
				name = rule;
			} else {
				name = aryRule[1];
			}
			if (map.containsKey(name)) {
				Object obj = map.get(name);
				if (obj != null) {
					try {
						titleRule = titleRule.replace(tag, obj.toString());
					} catch (Exception e) {
						titleRule = titleRule.replace(tag, "");
					}
				} else {
					titleRule = titleRule.replace(tag, "");
				}
			} else {
				//使用Groovy脚本引擎先解析
				String defaultValue = "";
				try {
					if(aryRule.length>1) {
						GroovyScriptEngine scriptEngine = AppUtil.getBean(GroovyScriptEngine.class);
						String script = rule.replace(aryRule[0]+":", "");
						defaultValue = scriptEngine.executeString(script, map);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
				titleRule = titleRule.replace(tag, defaultValue);
			}
		}
		return titleRule;
	}


}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值