Java工具

目录

1. 中文工具

2. 日期格式

3. FeignInterceptor过滤器

4. GlobalFilter全局过滤器

5. Http客户端操作

6. IP 限流

7. URLFilter路径校验

8. IdWork 分布式自增长ID

9. JWT工具

10. MQ消息封装

11. Page分页

12. Result结果集封装

13. TokenDecode公钥令牌解析

14. 工具库依赖pom.xml


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>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值