目录
1. 中文工具
package entity;
import java.io.UnsupportedEncodingException;
import java.util.Random;
public class ChineseUtils {
private static Random random = null;
private static Random getRandomInstance() {
if (random == null) {
random = new Random(System.currentTimeMillis());
}
return random;
}
public static String getChinese() {
String str = null;
int highPos, lowPos;
Random random = getRandomInstance();
highPos = (176 + Math.abs(random.nextInt(39)));
lowPos = 161 + Math.abs(random.nextInt(93));
byte[] b = new byte[2];
b[0] = (new Integer(highPos)).byteValue();
b[1] = (new Integer(lowPos)).byteValue();
try {
str = new String(b, "GB2312");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return str;
}
public static String getFixedLengthChinese(int length) {
String str = "";
for (int i = length; i > 0; i--) {
str = str + ChineseUtils.getChinese();
}
return str;
}
public static String getRandomLengthChiness(int start, int end) {
String str = "";
int length = new Random().nextInt(end + 1);
if (length < start) {
str = getRandomLengthChiness(start, end);
} else {
for (int i = 0; i < length; i++) {
str = str + getChinese();
}
}
return str;
}
public static void main(String args[]) {
System.out.println(ChineseUtils.getChinese());
System.out.println(ChineseUtils.getFixedLengthChinese(20));
System.out.println(ChineseUtils.getRandomLengthChiness(2, 5));
}
}
2. 日期格式
package entity;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class DateUtil {
// 时间格式
public static final String PATTERN_YYYYMMDDHH = "yyyyMMddHH";
public static final String PATTERN_YYYY_MM_DDHHMM = "yyyy-MM-dd HH:mm";
/**
* 从yyyy-MM-dd HH:mm格式转成yyyyMMddHH格式
*
* @param dateStr
* @return
*/
public static String formatStr(String dateStr, String opattern, String npattern) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(opattern);
try {
Date date = simpleDateFormat.parse(dateStr);
simpleDateFormat = new SimpleDateFormat(npattern);
return simpleDateFormat.format(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取指定日期的凌晨
*
* @return
*/
public static Date toDayStartHour(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
Date start = calendar.getTime();
return start;
}
/**
* 时间增加N分钟
*
* @param date
* @param minutes
* @return
*/
public static Date addDateMinutes(Date date, int minutes) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MINUTE, minutes);// 24小时制
date = calendar.getTime();
return date;
}
/**
* 时间递增N小时
*
* @param hour
* @return
*/
public static Date addDateHour(Date date, int hour) {//Jota-time
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.HOUR, hour);// 24小时制
date = calendar.getTime();
return date;
}
/**
* 获取时间菜单
*
* @return
*/
public static List<Date> getDateMenus() {
//定义一个List<Date>集合,存储所有时间段
List<Date> dates = getDates(12);
//判断当前时间属于哪个时间范围
Date now = new Date();
for (Date cdate : dates) {
//开始时间<=当前时间<开始时间+2小时
if (cdate.getTime() <= now.getTime() && now.getTime() < addDateHour(cdate, 2).getTime()) {
now = cdate;
break;
}
}
//当前需要显示的时间菜单
List<Date> dateMenus = new ArrayList<Date>();
for (int i = 0; i < 5; i++) {
dateMenus.add(addDateHour(now, i * 2));
}
return dateMenus;
}
/**
* 指定时间往后N个时间间隔
*
* @param hours
* @return
*/
public static List<Date> getDates(int hours) {
List<Date> dates = new ArrayList<Date>();
//循环12次
Date date = toDayStartHour(new Date()); //凌晨
for (int i = 0; i < hours; i++) {
//每次递增2小时,将每次递增的时间存入到List<Date>集合中
dates.add(addDateHour(date, i * 2));
}
return dates;
}
/**
* 时间转成yyyyMMddHH
*
* @param date
* @param pattern
* @return
*/
public static String data2str(Date date, String pattern) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
return simpleDateFormat.format(date);
}
}
3. FeignInterceptor过滤器
(微服务间过滤器在Feign调用前调用)
package entity;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.Enumeration;
public class FeignInterceptor implements RequestInterceptor {
/**
* feign 执行之前进行拦截
*
* 在实现购物车功能之前,要先明确一个问题,只有登录过的用户才可以访问自己的购物车。
* 所以我们在访问微服务的时候必须要携带令牌,但是还涉及到微服务之间的Feign接口调用,
* 令牌该怎么传递过去呢?令牌不是放在名为“Authorization”的请求头中吗,
* 所以加一个过滤器在Feign调用前调用,将当前请求的请求头信息封装到Feign请求的请求头中。
* 但是呢,这个过滤器是很多微服务共用的,所以可以将这个过滤器放在common工程中,
* 哪个微服务需要就直接注入到Spring容器中
*
*
* 如果哪个微服务想要调用的话,就直接在启动类中注入即可。
*
* @Bean
* public FeignHeaderInterceptor feignHeaderInterceptor() {
* return new FeignHeaderInterceptor();
* }
*
*
* @param template
*/
@Override
public void apply(RequestTemplate template) {
// 获取用户令牌,将令牌封装到头文件中
// 记录了当前用户请求的所有数据,包含请求头和请求参数等
// 注意默认是线程池隔离影响会get null
// 用户当前请求的时候对应线程的数据,如果开启了熔断,默认是线程池隔离,会开启新的线程,需要将熔断策略换成信号量隔离,此时不会开启新的线程
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// 获取请求头中的数据和所有头的名字
Enumeration<String> headerNames = requestAttributes.getRequest().getHeaderNames();
while (headerNames.hasMoreElements()) {
// 请求头的 key
String headerKey = headerNames.nextElement();
// 获取请求头的值
String headerValue = requestAttributes.getRequest().getHeader(headerKey);
System.out.println(headerKey + ": " + headerValue);
// 将请求头信息封装到头中,使用feign调用时,会传递到下一个微服务中
template.header(headerKey, headerValue);
}
}
}
4. GlobalFilter全局过滤器
package com.changgou.filter;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
/**
* 令牌 header 名字
*/
private static final String AUTHORIZE_TOKEN = "Authorization";
/**
* 用户登录地址
*/
private static final String USER_LOGIN_URL = "http://localhost:9001/oauth/login";
/**
* 全局拦截
*
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 用户如果是登录或者一些不需要权限认证的请求,直接放行
String uri = request.getURI().toString();
if (URLFilter.hasAuthorize(uri)) {
return chain.filter(exchange);
}
// header 中获取用户令牌信息
String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN);
// hasToken true:令牌在头文件中,false不在头文件中->将令牌封装到头文件中,再传递给其他微服务
boolean hasToken = true;
// 参数中获取用户令牌信息
if (StringUtils.isEmpty(token)) {
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
hasToken = false;
}
// cookie 中获取用户令牌信息
if (StringUtils.isEmpty(token)) {
HttpCookie httpCookie = request.getCookies().getFirst(AUTHORIZE_TOKEN);
if (httpCookie != null) {
token = httpCookie.getValue();
}
}
// 令牌为空,则允许访问,直接拦截
if (StringUtils.isEmpty(token)) {
// 设置没有权限的状态码 401
// response.setStatusCode(HttpStatus.UNAUTHORIZED);
// // 响应空数据
// return response.setComplete();
return needAuthorization(USER_LOGIN_URL + "?FROM=" + request.getURI(), exchange);
}
/* 访问微服务的JWT令牌是放在请求头中一个叫“Authorization”的参数中。以“bearer”开头。
因为请求是通过网关转发给相应的微服务,所以可以对网关进行配置。之前在网关微服务中写了一个过滤器叫AuthorizeFilter,
里面的**filter()**方法写的是分别从请求头、参数、Cookie中获取token信息,然后进行校验。现在稍微修改一下,
现在不校验了,只判断有无token信息并进行简单处理。*/
// else {
// if (!hasToken) {
// // 判断当前令牌是否有 bearer 前缀,如果没有则添加
// if (!token.startsWith("bearer ") && !token.startsWith("Bearer ")) {
// token = "bearer " + token;
// }
// // 将令牌封装到头文件中
// request.mutate().header(AUTHORIZE_TOKEN, token);
request.mutate().header(AUTHORIZE_TOKEN, "Bearer " + token);
// }
// }
// 有效则放行
return chain.filter(exchange);
}
/**
* 响应设置
*
* @param url
* @param exchange
* @return
*/
public Mono<Void> needAuthorization(String url, ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.SEE_OTHER);
response.getHeaders().set("Location", url);
return exchange.getResponse().setComplete();
}
/**
* 排序
*
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
5. Http客户端操作
package entity;
import com.github.wxpay.sdk.WXPayUtil;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class HttpClient {
private String url;
private Map<String, String> param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;
public boolean isHttps() {
return isHttps;
}
public void setHttps(boolean isHttps) {
this.isHttps = isHttps;
}
public String getXmlParam() {
return xmlParam;
}
public void setXmlParam(String xmlParam) {
this.xmlParam = xmlParam;
}
public HttpClient(String url, Map<String, String> param) {
this.url = url;
this.param = param;
}
public HttpClient(String url) {
this.url = url;
}
public void setParameter(Map<String, String> map) {
param = map;
}
public void addParameter(String key, String value) {
if (param == null)
param = new HashMap<String, String>();
param.put(key, value);
}
public void post() throws ClientProtocolException, IOException {
HttpPost http = new HttpPost(url);
setEntity(http);
execute(http);
}
public void put() throws ClientProtocolException, IOException {
HttpPut http = new HttpPut(url);
setEntity(http);
execute(http);
}
public void get() throws ClientProtocolException, IOException {
if (param != null) {
StringBuilder url = new StringBuilder(this.url);
boolean isFirst = true;
for (String key : param.keySet()) {
if (isFirst) {
url.append("?");
} else {
url.append("&");
}
url.append(key).append("=").append(param.get(key));
}
this.url = url.toString();
}
HttpGet http = new HttpGet(url);
execute(http);
}
/**
* set http post,put param
*/
private void setEntity(HttpEntityEnclosingRequestBase http) {
if (param != null) {
List<NameValuePair> nvps = new LinkedList<NameValuePair>();
for (String key : param.keySet()) {
nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
}
http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
}
if (xmlParam != null) {
http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
}
}
private void execute(HttpUriRequest http) throws ClientProtocolException,
IOException {
CloseableHttpClient httpClient = null;
try {
if (isHttps) {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
@Override
public boolean isTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext);
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
.build();
} else {
httpClient = HttpClients.createDefault();
}
CloseableHttpResponse response = httpClient.execute(http);
try {
if (response != null) {
if (response.getStatusLine() != null) {
statusCode = response.getStatusLine().getStatusCode();
}
HttpEntity entity = response.getEntity();
// 响应内容
content = EntityUtils.toString(entity, Consts.UTF_8);
}
} finally {
response.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.close();
}
}
public int getStatusCode() {
return statusCode;
}
/* *//**
* 发送 http 请求,获取结果
*
* @param paramMap
* @param url
* @return
* @throws Exception
*//*
private Map<String, String> getHttpResult(Map<String, String> paramMap, String url) throws Exception {
// 将Map数据转成XML字符
String xmlParam = WXPayUtil.generateSignedXml(paramMap, partnerkey);
// 发送请求
HttpClient httpClient = new HttpClient(url);
// 提交参数
httpClient.setXmlParam(xmlParam);
// 提交
httpClient.post();
// 获取返回数据
String content = httpClient.getContent();
// 将返回数据解析成Map
Map<String, String> resultMap = WXPayUtil.xmlToMap(content);
return resultMap;
}*/
public String getContent() throws ParseException, IOException {
return content;
}
}
6. IP 限流
package com.changgou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Objects;
@SpringBootApplication
@EnableEurekaClient
public class GatewayWebApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayWebApplication.class, args);
}
/**
* IP 限流
*
* @return
*/
@Bean(name = "ipKeyResolver")
public KeyResolver userKeyResolver() {
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
// 获取远程客户端 IP
String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostString();
System.out.println("用户请求的 IP:" + ip);
return Mono.just(ip);
}
};
}
}
spring:
application:
name: gateway-web
#Redis配置
redis:
host: 192.168.110.112
port: 6379
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" #跨域处理 允许所有的域
allowedMethods: # 支持的方法
- GET
- POST
- PUT
- DELETE
routes:
# 唯一标识符
- id: changgou_goods_route
# 用户请求需要路由到该服务
# uri: http://localhost:18081
# lb:使用 LoadBalanceClient 实现负载均衡,后面 goods 是微服务名称(主要用于集群环境)
uri: lb://goods
# 路由断言,路由规则配置
predicates:
# 用户请求的域名规则配置,所有 cloud.itheima.com 的请求都将被路由到 http://localhost:18081
# - Host=cloud.itheima.com**
# 所有以 /api/brand 的请求,都被路由 http://localhost:18081 微服务
# - Path=/api/brand/**
- Path=/api/goods/**
# 希望该路径由微服务网关自动添加上 /api 前缀
filters:
# 将请求路径中的第一个路径去掉,请求路径以 / 区分,
- StripPrefix=1
# 用户请求 /**->/brand/**, 并且将请求路由到 http://localhost:18081 微服务
# - PrefixPath=/brand
# 请求数限流, 名字不能随便写, 使用默认的 factory
- name: RequestRateLimiter
args:
# 用户身份唯一识别标识符
key-resolver: "#{@ipKeyResolver}"
# 每秒钟只允许有 1 个请求
redis-rate-limiter.replenishRate: 1
# 允许并发有 4 个请求[宽限的个数]
redis-rate-limiter.burstCapacity: 4
- id: changgou_order_route
uri: lb://order
predicates:
- Path=/api/cart/**,/api/categoryReport/**,/api/orderConfig/**,/api/order/**,/api/orderItem/**,/api/orderLog/**,/api/preferential/**,/api/returnCause/**,/api/returnOrder/**,/api/returnOrderItem/**
filters:
- StripPrefix=1
- id: changgou_user_route
uri: lb://user
predicates:
- Path=/api/user/**,/api/address/**,/api/areas/**,/api/cities/**,/api/provinces/**
filters:
- StripPrefix=1
server:
port: 8001
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer-ip-address: true
management:
endpoint:
gateway:
enabled: true
web:
exposure:
include: true
7. URLFilter路径校验
package com.changgou.filter;
public class URLFilter {
//有些请求比如登录注册等不需要token的就直接放行
private static final String ALL_URL = "/api/user/login,/api/user/add";
/**
* 校验当前访问路径是否需要权限
*
* @param url 获取到的当前的请求的地址
* @return
*/
public static boolean hasAuthorize(String url) {
String[] urls = ALL_URL.split(",");
for (String uri : urls) {
if (uri.equals(url)) {
return true;
}
}
return false;
}
}
8. IdWork 分布式自增长ID
package entity;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
/**
* <p>名称:IdWorker.java</p>
* <p>描述:分布式自增长ID</p>
* <pre>
* Twitter 的 Snowflake Java 实现方案
* </pre>
* 核心代码为其 IdWorker 这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:
* 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
* 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,
* 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),
* 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
* 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),
* 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
* <p>
* 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))
*
* @author Polim
*/
public class IdWorker {
// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
private final static long twepoch = 1288834974657L;
// 机器标识位数
private final static long workerIdBits = 5L;
// 数据中心标识位数
private final static long datacenterIdBits = 5L;
// 机器ID最大值
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 数据中心ID最大值
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 毫秒内自增位
private final static long sequenceBits = 12L;
// 机器ID偏左移12位
private final static long workerIdShift = sequenceBits;
// 数据中心ID左移17位
private final static long datacenterIdShift = sequenceBits + workerIdBits;
// 时间毫秒左移22位
private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
/* 上次生产id时间戳 */
private static long lastTimestamp = -1L;
// 0,并发控制
private long sequence = 0L;
private final long workerId;
// 数据标识id部分
private final long datacenterId;
public IdWorker() {
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
/**
* @param workerId 工作机器ID
* @param datacenterId 序列号
*/
public IdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* 获取下一个ID
*
* seckillOrder.setId(idWorker.nextId());
*
* @return
*/
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
// 当前毫秒内,则+1
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 当前毫秒内计数满了,则等待下一秒
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
// ID偏移组合生成最终的ID,并返回ID
long nextId = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
return nextId;
}
private long tilNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
/**
* 获取 maxWorkerId
*/
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
StringBuffer mpid = new StringBuffer();
mpid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (!name.isEmpty()) {
/*
* GET jvmPid
*/
mpid.append(name.split("@")[0]);
}
/*
* MAC + PID 的 hashcode 获取16个低位
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
/**
* 数据标识id部分
*/
protected static long getDatacenterId(long maxDatacenterId) {
long id = 0L;
try {
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null) {
id = 1L;
} else {
byte[] mac = network.getHardwareAddress();
id = ((0x000000FF & (long) mac[mac.length - 1])
| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
} catch (Exception e) {
System.out.println(" getDatacenterId: " + e.getMessage());
}
return id;
}
public static void main(String[] args) {
// 推特 26万个不重复的ID
IdWorker idWorker = new IdWorker(0, 0);
for (int i = 0; i < 2600; i++) {
System.out.println(idWorker.nextId());
}
}
}
9. JWT工具
package entity;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
public class JwtUtil {
/**
* 有效期为 1 hour
*/
public static final Long JWT_TTL = 3600000L;
/**
* Jwt令牌信息
*/
public static final String JWT_KEY = "itcast";
/**
*
* @param id
* @param subject
* @param ttlMillis
* @return
*
* @GetMapping("/login")
* public Result login(String username, String password, HttpServletResponse response, HttpServletRequest request) {
* // 查询用户信息
* User user = userService.findById(username);
*
* // 对比密码, 将明文密码加密后对比数据库中的加密密码
* if (BCrypt.checkpw(password, user.getPassword())) {
* // 创建用户令牌信息
* Map<String, Object> tokenMap = new HashMap<>();
* tokenMap.put("role","USER");
* tokenMap.put("success","SUCCESS");
* tokenMap.put("username",username);
* // 生成令牌
* String token = JwtUtil.createJWT(UUID.randomUUID().toString(), JSON.toJSONString(tokenMap), null);
*
* // 将令牌信息存入 Cookie 中
* Cookie cookie = new Cookie("Authorization", token);
* cookie.setDomain("localhost");
* cookie.setPath("/");
* response.addCookie(cookie);
*
* // 密码匹配,登陆成功
* return new Result(true, StatusCode.OK, "登陆成功!", token);
* }
*
* // 密码匹配失败,登陆失败
* return new Result(false, StatusCode.LOGINERROR, "账号或密码有误!");
* }
*/
public static String createJWT(String id, String subject, Long ttlMillis) {
// 指定算法
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 当前系统时间
long nowMillis = System.currentTimeMillis();
// 令牌签发时间
Date now = new Date(nowMillis);
// 如果令牌有效期为 null,则默认设置有效期 1 小时
if (ttlMillis == null) {
ttlMillis = JwtUtil.JWT_TTL;
}
// 令牌过期时间设置
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
// 生成秘钥
SecretKey secretKey = generalKey();
// 封装 Jwt 令牌信息
JwtBuilder builder = Jwts.builder()
// 唯一的 ID
.setId(id)
// 主题, 可以是JSON数据
.setSubject(subject)
// 签发者
.setIssuer("admin")
// 签发时间
.setIssuedAt(now)
// 签名算法以及密匙
.signWith(signatureAlgorithm, secretKey)
// 设置过期时间
.setExpiration(expDate);
return builder.compact();
}
/**
* 生成加密 secretKey
*
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getEncoder().encode(JwtUtil.JWT_KEY.getBytes());
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
/**
* 解析令牌数据
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
}
}
10. MQ消息封装
package entity;
import com.alibaba.fastjson.annotation.JSONField;
import java.io.Serializable;
/*****
* @Description: entity: MQ消息封装
****/
public class Message implements Serializable {
/**
* 执行的操作 1:增加,2:修改,3:删除
*/
private int code;
/**
* 数据
*/
private Object content;
/**
* 发送的 routkey
*/
@JSONField(serialize = false)
private String routekey;
/**
* 交换机
*/
@JSONField(serialize = false)
private String exchange;
public Message() {
}
public Message(int code, Object content) {
this.code = code;
this.content = content;
}
public Message(int code, Object content, String routekey, String exchange) {
this.code = code;
this.content = content;
this.routekey = routekey;
this.exchange = exchange;
}
public String getRoutekey() {
return routekey;
}
public void setRoutekey(String routekey) {
this.routekey = routekey;
}
public String getExchange() {
return exchange;
}
public void setExchange(String exchange) {
this.exchange = exchange;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Object getContent() {
return content;
}
public void setContent(Object content) {
this.content = content;
}
}
11. Page分页
package entity;
import java.io.Serializable;
import java.util.List;
public class Page<T> implements Serializable {
// 页数(第几页)
private long currentpage;
// 查询数据库里面对应的数据有多少条
private long total;// 从数据库查处的总记录数
// 每页查5条
private int size;
// 下页
private int next;
private List<T> list;
// 最后一页
private int last;
private int lpage;
private int rpage;
//从哪条开始查
private long start;
//全局偏移量
public int offsize = 2;
public Page() {
super();
}
/****
*
* @param currentpage
* @param total
* @param pagesize
*/
public void setCurrentpage(long currentpage, long total, long pagesize) {
//可以整除的情况下
long pagecount = total / pagesize;
//如果整除表示正好分N页,如果不能整除在N页的基础上+1页
int totalPages = (int) (total % pagesize == 0 ? total / pagesize : (total / pagesize) + 1);
//总页数
this.last = totalPages;
//判断当前页是否越界,如果越界,我们就查最后一页
if (currentpage > totalPages) {
this.currentpage = totalPages;
} else {
this.currentpage = currentpage;
}
//计算start
this.start = (this.currentpage - 1) * pagesize;
}
//上一页
public long getUpper() {
return currentpage > 1 ? currentpage - 1 : currentpage;
}
//总共有多少页,即末页
public void setLast(int last) {
this.last = (int) (total % size == 0 ? total / size : (total / size) + 1);
}
/****
* 带有偏移量设置的分页
* @param total
* @param currentpage
* @param pagesize
* @param offsize
*/
public Page(long total, int currentpage, int pagesize, int offsize) {
this.offsize = offsize;
initPage(total, currentpage, pagesize);
}
/****
*
* @param total 总记录数
* @param currentpage 当前页
* @param pagesize 每页显示多少条
*/
public Page(long total, int currentpage, int pagesize) {
initPage(total, currentpage, pagesize);
}
/****
* 初始化分页
* @param total
* @param currentpage
* @param pagesize
*/
public void initPage(long total, int currentpage, int pagesize) {
//总记录数
this.total = total;
//每页显示多少条
this.size = pagesize;
//计算当前页和数据库查询起始值以及总页数
setCurrentpage(currentpage, total, pagesize);
//分页计算
int leftcount = this.offsize, //需要向上一页执行多少次
rightcount = this.offsize;
//起点页
this.lpage = currentpage;
//结束页
this.rpage = currentpage;
//2点判断
this.lpage = currentpage - leftcount; //正常情况下的起点
this.rpage = currentpage + rightcount; //正常情况下的终点
//页差=总页数和结束页的差
int topdiv = this.last - rpage; //判断是否大于最大页数
/***
* 起点页
* 1、页差<0 起点页=起点页+页差值
* 2、页差>=0 起点和终点判断
*/
this.lpage = topdiv < 0 ? this.lpage + topdiv : this.lpage;
/***
* 结束页
* 1、起点页<=0 结束页=|起点页|+1
* 2、起点页>0 结束页
*/
this.rpage = this.lpage <= 0 ? this.rpage + (this.lpage * -1) + 1 : this.rpage;
/***
* 当起点页<=0 让起点页为第一页
* 否则不管
*/
this.lpage = this.lpage <= 0 ? 1 : this.lpage;
/***
* 如果结束页>总页数 结束页=总页数
* 否则不管
*/
this.rpage = this.rpage > last ? this.last : this.rpage;
}
public long getNext() {
return currentpage < last ? currentpage + 1 : last;
}
public void setNext(int next) {
this.next = next;
}
public long getCurrentpage() {
return currentpage;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public long getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public long getLast() {
return last;
}
public long getLpage() {
return lpage;
}
public void setLpage(int lpage) {
this.lpage = lpage;
}
public long getRpage() {
return rpage;
}
public void setRpage(int rpage) {
this.rpage = rpage;
}
public long getStart() {
return start;
}
public void setStart(long start) {
this.start = start;
}
public void setCurrentpage(long currentpage) {
this.currentpage = currentpage;
}
/**
* @return the list
*/
public List<T> getList() {
return list;
}
/**
* @param list the list to set
*/
public void setList(List<T> list) {
this.list = list;
}
}
12. Result结果集封装
package entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
@ApiModel(description = "Result", value = "Result")
public class Result<T> implements Serializable {
@ApiModelProperty(value = "执行是否成功, true: 成功, false: 失败", required = true)
private boolean flag;
@ApiModelProperty(value = "返回状态码, 20000: 成功, 20001: 失败, 20002: 用户名或密码错误, 20003: 权限不足, 20004: 远程调用失败, 20005: 重复操作, 20006: 没有对应的抢购数据", required = true)
private Integer code;
@ApiModelProperty(value = "提示信息", required = true)
private String message;
@ApiModelProperty(value = "返回数据", required = true)
private T data;
public Result(boolean flag, Integer code, String message, Object data) {
this.flag = flag;
this.code = code;
this.message = message;
this.data = (T) data;
}
public Result(boolean flag, Integer code, String message) {
this.flag = flag;
this.code = code;
this.message = message;
}
public Result() {
this.flag = true;
this.code = StatusCode.OK;
this.message = "操作成功!";
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
13. TokenDecode公钥令牌解析
package entity;
import com.alibaba.fastjson.JSON;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.util.StringUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.stream.Collectors;
public class TokenDecode {
// 公钥
private static final String PUBLIC_KEY = "public.key";
private static String publickey = "";
/**
* 获取非对称加密公钥 Key
*
* @return 公钥 Key
*/
public static String getPubKey() {
if (!StringUtils.isEmpty(publickey)) {
return publickey;
}
Resource resource = new ClassPathResource(PUBLIC_KEY);
try {
InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
BufferedReader br = new BufferedReader(inputStreamReader);
publickey = br.lines().collect(Collectors.joining("\n"));
return publickey;
} catch (IOException ioe) {
return null;
}
}
/**
* 解析令牌数据
*
* @param token
* @return
*/
public static Map<String, String> dcodeToken(String token) {
// 校验Jwt
Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(getPubKey()));
// 获取Jwt原始内容
String claims = jwt.getClaims();
return JSON.parseObject(claims, Map.class);
}
/**
* 获取令牌信息
*
* @GetMapping("/list")
* // @PreAuthorize("hasAnyAuthority('USER')") //@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
* public Result<List<OrderItem>> list() {
* // 用户的令牌信息->解析令牌信息->username
* Map<String, String> userInfo = TokenDecode.getUserInfo();
* System.out.println(userInfo);
* String username = userInfo.get("username");
*
* // 查询购物车列表
* List<OrderItem> orderItems = cartService.list(username);
* return new Result<List<OrderItem>>(true, StatusCode.OK, "购物车列表查询成功!", orderItems);
* }
*
*
*
* @return
*/
public static Map<String, String> getUserInfo() {
// 获取授权信息
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails();
// 令牌解码
return dcodeToken(details.getTokenValue());
}
}
14. 工具库依赖pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>changgou-parent</artifactId>
<groupId>com.changgou</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>changgou-common</artifactId>
<description>工具工程</description>
<dependencies>
<!-- web起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- redis 使用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 微信支付 -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<!-- httpclient 支持 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- compile-->
<!-- 默认的scope,表示 dependency 都可以在生命周期中使用。
而且,这些dependencies 会传递到依赖的项目中。适用于所有阶段,会随着项目一起发布-->
<!-- provided-->
<!-- 跟compile相似,但是表明了dependency 由JDK或者容器提供,例如Servlet AP和一些Java EE APIs。
这个scope 只能作用在编译和测试时,同时没有传递性。-->
<!-- runtime-->
<!-- 表示dependency不作用在编译时,但会作用在运行和测试时,如JDBC驱动,适用运行和测试阶段。
test表示dependency作用在测试时,不作用在运行时。 只在测试时使用,用于编译和运行测试代码。不会随项目发布。
system跟provided 相似,但是在系统中要以外部JAR包的形式提供,maven不会在repository查找它-->
<!--oauth依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<scope>provided</scope>
</dependency>
<!--鉴权-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>