8月6
今天有个同事说要我帮他看下怎么把这个定时任务的cron表达式放到配置文件里面去,
1、在类上加注解@Component,交给Spring管理
2、在@PropertySource指定配置文件名称,如下配置,指定放在src/main/resource目录下的application.yml文件
3、配置文件application.yml参考内容如下
application.yml

jobs:
  customDictUpdateTime:
    corn: "0/5 * * * * ?"


@Configuration
@Component
@Slf4j
@PropertySource(value = "classpath:application.yml")
public class QuartzTask {

    @Autowired
    private RedisUtil redisUtil;

    private static boolean startTask = true;

    @PostConstruct
    public void startInit() {
        excuteBusiness();
    }

    //定时任务初始化政务宝自定义词库
   //如果获取不到, 取冒号后面的默认值
    @Scheduled(cron = "${jobs.customDictUpdateTime.corn:0/5 * * * * ?}")
    public void excuteTask() {
        excuteBusiness();
    }



上面说的是yml配置文件,有的同学就说了,那么我用的application.properties文件配置呢?

application.properties

jobs.customDictUpdateTime.corn=0/5 * * * *

import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;


@Configuration
@Component
@Slf4j
@PropertySource(value = "classpath:application.properties")
public class QuartzTask {

    @Autowired
    private RedisUtil redisUtil;

    private static boolean startTask = true;

    @PostConstruct
    public void startInit() {
        excuteBusiness();
    }

    //定时任务初始化政务宝自定义词库
   //如果获取不到, 取冒号后面的默认值
    @Scheduled(cron = "${jobs.customDictUpdateTime.corn:0/5 * * * * ?}")
    public void excuteTask() {
        excuteBusiness();
    }



7月2
最近有个项目线上运行的时候客户说你们是不是限制了1M的,我说没有啊,原来发现是nginx报的。
从字面上看,说的是请求的实体太大的问题,那么可以联想到是HTTP请求中的Body大小被限制了的原因。

Nginx中的【client_max_body_size】配置属性

通过查资料,发现是Nginx配置中限制了请求的实体大小,因此就可以通过修改Nginx的配置文件来解决这个问题。Nginx的默认配置文件是conf目录下的nginx.conf文件,如果有自行扩展的配置文件可以在nginx.conf文件中查找include关键字去定位到相应的扩展配置文件。

具体的话是有一个【client_max_body_size】属性,这个属性可以配置在http节点下(http全局),可以配置在server节点下(server全局),也可以配置在location节点下(单应用)。要注意的是,这个属性在不配置的情况下默认值是1m,也就是限制了请求实体的大小为1m。

http节点下:

http {
    # 将Nginx代理的所有请求实体的大小限制为20m
    client_max_body_size 20m;
}
server节点下:

server {
    # 将该服务下的所有请求实体的大小限制为20m
    client_max_body_size 20m;
}
location节点下:

location /yanggb {
    # 将此路由请求的实体大小限制为20m
    client_max_body_size 20m;
}
保存之后要记得重启Nginx使修改后的配置生效。

service nginx restart
Tomcat的【maxPostSize】配置属性

另外的,Tomcat下的conf文件夹中的server.xml文件中也有属性配置【maxPostSize】可以限制post请求参数的大小。


<Connector port="8080" protocol="HTTP/1.1"  
    connectionTimeout="2000"  
    redirectPort="8443"  
    URIEncoding="UTF-8"  
    maxThreads="3000"  
    compression="on" compressableMimeType="text/html,text/xml"  
    maxPostSize="256"/>  
<Connection port="8009" enableLookups="false" redirectPort="8443" debug="0" protocol="AJP/1.3" />

要注意的是,在Tomcat7.0.63之前,maxPostSize属性设置为0和负数都可以代表不限制,但是之后的Tomcat版本只能将maxPostSize属性设置为负数才能代表不限制。
5月28
在开发中如果我们遇到这种需要验证的接口应该怎么调用呢?
这里给出一个Basic Authentication 接口调用的工具示例:
package com.heckjj.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;

import org.apache.commons.codec.binary.Base64;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;


public class GetAPIResultUtil {


    /**
     *
     *
     * @param url
     * @param param
     * @return
     */
    public static String getAPIResult(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            //conn.setConnectTimeout(5000);
            String plainCredentials = "heck:12345";
            String base64Credentials = new String(Base64.encodeBase64(plainCredentials.getBytes()));
            conn.setRequestProperty("Authorization", "Basic " + base64Credentials);
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("Content-type", "application/json");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }
}

至此对基本认证(Basic Authentication)的完整过程就基本实现了,希望对大家有所帮助!
2月18
关于Hutool工具类之HttpUtil如何使用可以参考官方文档Hutool之HttpUtil

其实使用Http和Https使用的方式是一样的。

建议大家可以看看HttpUtil的源码,感觉设计的挺不错的。

导入Maven依赖
<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-all</artifactId>
   <version>4.1.0</version>
</dependency>
编写测试类(使用Junit单元测试)

@Test
public void testHttps() throws Exception {
    
    JSONObject json = new JSONObject();
    json.put("username", "1332788xxxxxx");
    json.put("password", "123456.");
    
    String result = HttpRequest.post("https://api.heckjj.com/1/users")
            .header("Content-Type", "application/json")
            .header("X-heck-Application-Id","2f0419a31f9casdfdsf431f6cd297fdd3e28fds4af")
            .header("X-heck-REST-API-Key","1e03efdas82178723afdsafsda4be0f305def6708cc6")
            .body(json)
            .execute().body();
    
    
       System.out.println(result);
            
            
}

方法解释(上面采用的是一种叫链式编程的方式):
header对应的是请求头。
body对应的是请求体(包含参数和参数值)。
HttpRequest里面包含Post、GET、Delete、Put等常用的RestFul方式。

打印如下:

{"createdAt":"2019-04-30 10:42:07","objectId":"6cfdb77081","sessionToken":"269e433440c9e65b8058d016df703ccb"}
2月8
  /*验证IP是否属于某个IP段
   *
   * ipSection    IP段(以'-'分隔)
   * ip           所验证的IP号码
   *
   */
  public static boolean ipExistsInRange(String ip, String ipSection) {
    ipSection = ipSection.trim();
    ip = ip.trim();
    int idx = ipSection.indexOf('-');
    String beginIP = ipSection.substring(0, idx);
    String endIP = ipSection.substring(idx + 1);
    return getIp2long(beginIP) <= getIp2long(ip) && getIp2long(ip) <= getIp2long(endIP);
  }

  public static long getIp2long(String ip) {
    ip = ip.trim();
    String[] ips = ip.split("\\.");
    long ip2long = 0L;
    for (int i = 0; i < 4; ++i) {
      ip2long = ip2long << 8 | Integer.parseInt(ips[i]);
    }

    return ip2long;
  }

  public static long getIp2long2(String ip) {
    ip = ip.trim();
    String[] ips = ip.split("\\.");
    long ip1 = Integer.parseInt(ips[0]);
    long ip2 = Integer.parseInt(ips[1]);
    long ip3 = Integer.parseInt(ips[2]);
    long ip4 = Integer.parseInt(ips[3]);
    long ip2long = 1L * ip1 * 256 * 256 * 256 + ip2 * 256 * 256 + ip3 * 256 + ip4;

    return ip2long;
  }

  public static boolean isIntranetIP(String ip) {
  ipExistsInRange(ip, "172.16.0.0-172.31.255.255") || ip.startsWith("10.");
        //10.10.10.116 是否属于固定格式的IP段10.10.1.00-10.10.255.255

        String ip="10.10.10.116";

        String ipSection="10.10.1.00-10.10.255.255";

        boolean exists=ipExistsInRange(ip,ipSection);

        System.out.println(exists);

        System.out.println(getIp2long(ip));

        System.out.println(getIp2long2(ip));
  }
12月25
1:echo $JAVA_HOME

使用$JAVA_HOME的话能定位JDK的安装路径的前提是配置了环境变量$JAVA_HOME,否则如下所示,根本定位不到JDK的安装路径

[root@localhost ~]# java -version

java version "1.7.0_65"

OpenJDK Runtime Environment (rhel-2.5.1.2.el6_5-x86_64 u65-b17)

OpenJDK 64-Bit Server VM (build 24.65-b04, mixed mode)

[root@localhost ~]# echo $JAVA_HOME

点击在新窗口中浏览此图片

2:which java

首先要申明一下which java是定位不到安装路径的。which java定位到的是java程序的执行路径。那么怎么定位到java的安装路径呢?下面我们来看看例子吧,如下所示:
[root@localhost ~]# java -version
java version "1.7.0_51"
OpenJDK Runtime Environment (rhel-2.4.5.5.el7-x86_64 u51-b31)
OpenJDK 64-Bit Server VM (build 24.51-b03, mixed mode)
[root@localhost ~]# which java
/usr/bin/java
[root@localhost ~]# ls -lrt /usr/bin/java
lrwxrwxrwx. 1 root root 22 9月   6 22:51 /usr/bin/java -> /etc/alternatives/java
[root@localhost ~]# ls -lrt /etc/alternatives/java
lrwxrwxrwx. 1 root root 72 9月   6 22:51 /etc/alternatives/java -> /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.51-2.4.5.5.el7.x86_64/jre/bin/java

点击在新窗口中浏览此图片
Tags:
11月18
1.我为什么会出现这个问题?

意思是无法在根节点  Software\JavaSoft\Prefs 创建命令,实际上就是当前不是管理员权限在运行,需要管理员权限运行。

2.这个错大概的意思就是

警告:无法打开/创建prefs根节点软件\\javasoft \\prefs根节点0x800000002 (这里是百度翻译),但是里面的关键字就是说无法打开,那就代表着权限不够。

3.解决方案

Step1:搜索并运行regedit.exe

Step2: 进入HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft,右击JavaSoft目录,选择新建->项(key),命名为Prefs

Step3: 重新编译即可。
11月18
原因:
1. SecureRandom 类中 setSeed()底层调用的是 native 方法.所以造成了不同环境之间随机数出现了差别。导致解密不一致问题。
2. 由于linux和window的内核不同造成的!

解决:对加密程序 添加如下两行 代码控制 随机数即可解决问题。然后初始化,就能解决这个问题!
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(PASSWORD.getBytes());

示例如下:
package com.heckjj.utils;


import java.security.SecureRandom;


import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;


import org.apache.axis.encoding.Base64;
import org.apache.commons.lang.StringUtils;


import lombok.extern.slf4j.Slf4j;


@Slf4j
public class AESUtils {


    /**
     * 加密
     *
     * @param content 需要加密的内容
     * @param secureKey 加密秘钥
     * @return
     */
    public static String encrypt(String content, String secureKey) {
        try {
            if (StringUtils.isEmpty(content)
                        || StringUtils.isEmpty(secureKey)) {
                return null;
            }


            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            /*
             * 问题我已自己解决,这个是由于linux和window的内核不同造成的!
             * SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
             * secureRandom.setSeed(PASSWORD.getBytes());
             * 然后初始化,就能解决这个问题!
             */
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(secureKey.getBytes());
            kgen.init(128, secureRandom);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            Cipher cipher = Cipher.getInstance("AES");// 创建密码器
            byte[] byteContent = content.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
            byte[] result = cipher.doFinal(byteContent);
            return encodeBASE64(result); // 加密
        } catch (Exception e) {
            log.error("加密错误.", e);
        }
        return null;
    }


    /**
     * 解密
     *
     * @param content 待解密内容
     * @param password secureKey
     * @return
     */
    public static String decrypt(String content, String secureKey) {
        try {
            if (StringUtils.isEmpty(content) || StringUtils.isEmpty(secureKey)) {
                return null;
            }

            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(secureKey.getBytes());
            kgen.init(128, secureRandom);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            Cipher cipher = Cipher.getInstance("AES");// 创建密码器
            cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
            byte[] base64Dec = Base64.decode(content);
            byte[] result = cipher.doFinal(base64Dec);
            return new String(result);
        } catch (Exception e) {
            log.warn("解密错误,错误信息是:{}", e);
        }
        return null;
    }

    public static String encodeBASE64(byte[] content) throws Exception {
        if (content == null || content.length == 0)
            return null;
        try {
            return Base64.encode(content);
        } catch (Exception e) {
            log.error("Base64 encode error.", e);
            return null;
        }
    }

}
11月11
1.在idea中用maven将程序打成jar,放到运行的目录中。

2.去github上面下载winsw: https://github.com/kohsuke/winsw/releases
点击在新窗口中浏览此图片

3. 将WinSW.NET4.exe文件复制到java程序所在文件夹中


4.将java程序重命名,去掉名称中的“.”。例如hccabc-web-1.0.jar  ---->  hccabc-web.jar


5.将WinSW.exe重命名为hccabc-web.exe(和jar同名)


6. 新建一个xml文件,命名为hccabc-web.xml,写入以下内容(还有一些参数自己去看github说明):

     <id>hccabc-web</id>

     <name>hccabc-web</name>

     <description>鹤城区民政局便民救助平台.</description>

     <!-- java环境变量 -->

     <env name="JAVA_HOME" value="%JAVA_HOME%"/>

     <executable>java</executable>

    <arguments>-jar "E:\hccabc\hccabc-web\hccabc-web.war"</arguments>

     <!-- 开机启动 -->

     <startmode>Automatic</startmode>

     <!-- 日志配置 -->

     <logpath>%BASE%\logs</logpath>

     <logmode>rotate</logmode>

</service>


如果没有配置环境变量,直接将三个文件扔到java的bin目录下运行。去掉标签<env name="JAVA_HOME" value="%JAVA_HOME%"/>



7.命令行定位到当前目录,执行:

hccabc-web.exe  install


8. 去windows服务列表中启动程序。

(如果需要更新程序,只需要先将服务停止,再将新文件重命名为hccabc-web.jar,最后启动服务就行了)
10月14

昨天我们谈了怎么建立socket通信的服务端和客户端,今天我们就来谈一谈怎么封装报文。

什么是报文这里我就不在阐述了,不清楚的朋友可以自己去查资料。我们今天要谈的报文主要友以下几个部分组成:

3位同步校验位+8位报文长度+报文头+报文体+32位MD5校验位

基本格式如下:

0X110X120X1300000232<?xml version="1.0" encoding="GBK"?><ROOT><Code>0204</Code><Date>20141223</Date><No>141223010008152</No><Code>17010001</Code><Name>张三</Name></ROOT>B251AB76B11114DB176023A0AA27A524

说明:

  前面的0X110X120X13是3位16进制的同部位,这里为了大家理解,所以就以字符的形式谢出来了。00000232是报文长度。<?xml version="1.0" encoding="GBK"?><ROOT><Code>0204</Code><Date>20141223</Date><No>141223010008152</No><Code>17010001</Code></ROOT>是报文头。即每个报文都包含的信息。<Name>张三</Name>是报文体。B251AB76B11114DB176023A0AA27A524是加密数据。

关于如何将对象转换为xml格式的报文我将在下一篇写,这里主要是给大家如何将如上的这些字符串转化为字节以及如何发送和接收报文。

1.建立报文的对象

public class SocketPacket {


    private String bodyLen;

    private String body;

    private String syncStr;

    private String md5;

    public String getBodyLen() {

        return bodyLen;

    }

    public String getBody() {

        return body;

    }

    public String getSyncStr() {

        return syncStr;

    }

    public String getMd5() {

        return md5;

    }

    public void setBodyLen(String bodyLen) {

        this.bodyLen = bodyLen;

    }

    public void setBody(String body) {

        this.body = body;

    }

    public void setSyncStr(String syncStr) {

        this.syncStr = syncStr;

    }

    public void setMd5(String md5) {

        this.md5 = md5;

    }

    

    

    public byte[] getByteStream() throws UnsupportedEncodingException{

        byte[] bodyBytes = this.body.getBytes("gbk");//获得body的字节数组

        int bodyLength = bodyBytes.length;

        int socketLength = 3+bodyLength+8+32;

        byte [] soc = new byte[socketLength];

        //添加校验数据

        int index = 0;

        soc[0]=0x11;

        soc[1]=0x12;

        soc[2]=0x13;

        index+=3;

        //添加8位报文长度(我的博文中也有NumberFormat的用法介绍)

        NumberFormat numberFormat = NumberFormat.getNumberInstance();

        numberFormat.setMinimumIntegerDigits(8);

        numberFormat.setGroupingUsed(false);

        byte [] num = numberFormat.format(socketLength).getBytes();

        for(int i = 0;i<8;i++){

            soc[index++]= num[i];

        }

        //添加body内容

        for(int i = 0;i<bodyLength;i++){

            soc[index++] = bodyBytes[i];

        }

        //添加md5校验码

        byte [] md5Bytes = this.md5.getBytes();

        for (int i = 0; i < num.length; i++) {

            soc[index++] = md5Bytes[i];

        }

        return soc;

    }

    

    //字节装转报文string

    public String getString(byte [] socketBytes){

        String syncStr = this.bytesToString(socketBytes, 0, 3);

        String socketLength = this.bytesToString(socketBytes, 3, 3+8);

        String body = this.bytesToString(socketBytes, 3+8, socketBytes.length-32);

        String md5 = this.bytesToString(socketBytes,socketBytes.length-32,socketBytes.length);

        return syncStr+socketLength+body+md5;

    }

    

    //将字节数组转化为string

    public String bytesToString(byte [] bytes,int start,int end){

        String str = "";

        if(bytes.length<end-start){

            return str;

        }

        byte [] bs = new byte[end-start];

        for(int i = 0;i<end-start;i++){

            bs[i] = bytes[start++];

        }

        str = new String(bs);

        return str;

    }

    

    public String toString(){

        return this.syncStr+this.bodyLen+this.body+this.md5;

    }

    

}


2.封装发送和接收报文的工具类

/**

* 报文发送

*/

public class SockeUtil {

    Socket socket = null;

    public SockeUtil(String ip,int port) throws UnknownHostException, IOException{

        socket = new Socket(ip, port);

    }

    //

    public SocketPacket sentSocket(SocketPacket socketPacket) throws UnsupportedEncodingException, IOException{

        SocketPacket sPacket = new SocketPacket();

        OutputStream output=null;

        InputStream input =null;

        // 同步字符串(3byte)

        byte[] sync = null; //

        byte[] bodyLen = null; // 8位长度

        byte[] body = null; // 内容

        byte[] md5 = null;  // MD5

        output = socket.getOutputStream();

        //写数据发送报文

        output.write(socketPacket.getByteStream());

        //获得服务端返回的数据

        input = socket.getInputStream();

        sync = this.streamToBytes(input,3);

        bodyLen = this.streamToBytes(input, 8);

        String lenString = new String(bodyLen);

        int len = Integer.valueOf(lenString);

        body = this.streamToBytes(input, len);

        md5 = this.streamToBytes(input, 32);

        sPacket.setSyncStr(new String(sync,Charset.forName("gbk")));

        socketPacket.setBodyLen(new String(bodyLen,Charset.forName("gbk")));

        socketPacket.setBody(new String(body,Charset.forName("gbk")));

        socketPacket.setMd5(new String(md5,Charset.forName("gbk")));

        return sPacket;

    }

    

    public byte[] streamToBytes(InputStream inputStream,int len){

        /**

         * inputStream.read(要复制到得字节数组,起始位置下标,要复制的长度)

         * 该方法读取后input的下标会自动的后移,下次读取的时候还是从上次读取后移动到的下标开始读取

         * 所以每次读取后就不需要在制定起始的下标了

         */

        byte [] bytes= new byte[len];

        try {

            inputStream.read(bytes, 0, len);

        } catch (IOException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

        return bytes;

    }

}

3.在封装一个调用报文发送的类:

public String socket(SocketPackage socketPackage) throws UnsupportedEncodingException{

        

        SocketClient socketClient=null;;

        try {

            socketClient = new SocketClient(ip,端口);

        } catch (UnknownHostException e) {

            log.error("socket链接异常,链接信息:"+ip+端口);

            e.printStackTrace();

        } catch (IOException e) {

            log.error("socket IO异常");

            e.printStackTrace();

        }      

        SocketPackage s = null;

        try {

            s = socketClient.sendMsg(socketPackage);

        } catch (Exception e) {

            try {

                log.error("socket发送消息异常,发送信息:"+new String(socketPackage.getByteStream(),"GBK"));

            } catch (UnsupportedEncodingException e1) {

                log.error("socket将socketPackage转为字符串异常,socketPackage信息:"+socketPackage.getByteStream());

                e1.printStackTrace();

            }

            e.printStackTrace();

        }      

        String result = "";

        try {

             result = new  String(s.getStream(),"GBK");

        } catch (UnsupportedEncodingException e) {

            log.error("socket将socketPackage转为字符串异常,socketPackage信息:"+socketPackage.getByteStream());

            e.printStackTrace();

        }

        return result ;

  }

这样我们就能发送报文和接收报文了!赶紧试一下吧!^_^

分页: 5/20 第一页 上页 1 2 3 4 5 6 7 8 9 10 下页 最后页 [ 显示模式: 摘要 | 列表 ]