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 每一个点对应一串:  
*/
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 += "  ";
}
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("'", "'").replace(""", "\"")
.replace(">", ">").replace("<", "<")
.replace("&", "&");
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("<");
break;
case '>':
sb.append(">");
break;
case '&':
sb.append("&");
break;
case '\'':
sb.append("'");
break;
case '"':
sb.append(""");
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;
}
}