Object's Blog

12月踩坑记录(2019)

字数统计: 3.6k阅读时长: 15 min
2019/12/30 分享

前言

2019拜拜。

连接Oracle数据库异常:ORA-12541:TNS无监听程序

  • 问题描述

    这个异常属于突发事件,原因不明,不是程序的问题。

  • 解决方案

    1. 连接oracle

      su oracle

      sqlplus /nolog

      连接oracle

    2. 启动服务

      startup

      启动服务

    3. 切换到oracle/bin目录,启动监听

      cd $ORACLE_HOME/bin

      lsnrctl start

      启动监听

MyBatis动态SQL字符串判等

  • 问题描述

    使用MyBatis如果要对字符串进行判等,判等的字符串不能直接使用单引号包含,这样MyBatis会将它解析为字符,而java是强类型语言,这样是无法判等的。

  • 解决方案

    使用如下格式进行判等:

    1
    2
    <if test="sex=='Y'.toString()">
    <if test = 'sex== "Y"'>

    注意: 不能使用

    1
    2
    <if test="sex=='Y'">
    </if>

MyBatis+Oracle使用日期作为条件查询

  • 问题描述

    在一次使用MyBatis查询时,使用范围日期作为查询条件,出现了一个bug,假设要查询12.22当天的数据,那么我们会将查询条件设置为startDate>=12-22 and endDate<=12-22,那么查询出来的结果集为空,经过分析之后得出,oracle会将这样的条件解析为startDate>=12-22 00:00:00 and endDate<=12-22 00:00:00,所以无法查询出我们期望的结果集,在不考虑改变数据类型的情况时,有如下解决方案。

  • 解决方案

    ​ 使用trunc方法,将endDate<=12-22修改为endDate<=trunc(#{endDate})+1,就可以查询出正确的结果了。

MyBatis一对多查询

  • 问题描述

    在查询出的对象内部包含集合的定义。

    这不算是坑,只是一段时间不用这种方式来进行查询会很容易忘记,所以记录下来,下次再遇到不用再百度了。

  • 解决方案

    1
    2
    3
    4
    5
    6
    <collection column="product_no" property="callPhoneList" ofType="CallPhoneDomain">
    <result column="product_no" property="productNo"/>
    <result column="op_time" property="opTime"/>
    <result column="called_number" property="calledNumber"/>
    <result column="mou" property="mou"/>
    </collection>

JQuery的判空

  • 问题描述

    在java中的判空可以使用object==null来实现,非常方便,那么在JQuery中如何进行判空?

  • 解决方案

    1
    $.isEmptyObject(object);

Jedis存入Redis的数据,使用RedisTemplate无法取出

  • 问题描述

    这个月做了一个项目(SpringBoot),这个项目是作为一个老项目(SSM)的子项目存在的,所以从开始就没打算重新设计用户的认证模块,准备直接使用老项目的用户认证模块来进行这个项目的认证,因为老项目在用户登录后会在redis中维护一个在线用户列表,所以子项目也就可以直接去这个redis中获取在线用户,为了偷懒,就没有重新封装jedis工具类,想直接使用RedisTemplate来对redis进行操作,但是发现无论如何都获取不到老项目存在redis中的数据,经过排查后发现RedisTemplate在进行redis存取时都会自动将传入的参数序列化,但是jedis不会,所以导致无法取出对应的数据。

  • 解决方案

    由于老项目中使用的是jedis存入数据而且并没有进行序列化,所以在这个项目中也就只能用jedis来取数据了。

    其实也可以将老项目中存入redis的操作手动序列化,但是要改动的地方太多了,所以没有采取这个方案。

Jackson忽略属性

  • 问题描述

    场景:json串传来了20个属性,但是我只要其中的4个。

  • 解决方案

    在VO类或PO类上添加如下属性

    @JsonIgnoreProperties(ignoreUnknown = true)

SpringBoot环境下实现过滤器

  • 问题描述

    在SpringBoot的项目中写了一个过滤器,但是在调试时发现过滤器没有生效。

  • 解决方案

    SpringBoot环境下写过滤器一定要使用@Component注解,交给Spring托管,否则过滤器无法生效。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Component
    @WebFilter(filterName="AuthFilter",urlPatterns = "/*")
    public class AuthFilter implements Filter{

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {

    }

    }

父系统和子系统之间的单点授权登录

  • 问题描述

    最近在做一个项目,这个项目作为一个老项目的子项目存在(上文中有提到过)。

    父系统有独立的登录接口,子系统作为父系统的一部分,如何在父系统登录之后,访问子系统也能保持登录状态,而且子系统只能通过父系统进入,直接输入Url的方式无法访问?

  • 解决方案

    跟同事交流后,由于这个场景只有两个系统,所以不打算自己实现一个认证中心(当然之后自己研究了单点登录的一些知识,实现了一个认证中心,应用在自己的新项目上了)。

    针对这个场景,做出如下解决方案:

    当在父系统中点击进入子系统时,向子系统发送接入请求,如果子系统验证了该用户在父系统中已经登录(redis),那么向父系统返回一个状态码和token,并且将token存在redis中,父系统将token返回给浏览器,让浏览器带着这个token去访问子系统,当子系统的过滤器取到这个token,和redis中的token进行比对,如果相同,则直接放行,并删除token(相当于让这个token过期),然后设置session,之后这个用户再进入这个系统,由于存在session,就不需要再进行获取token的流程了,并且如果有别人拿着这个token进入子系统,由于这个token已经过期,就无法完成认证。

生成url可以携带的token

  • 问题描述

    还是认证的问题,上文中提到让浏览器携带token访问子系统,那么这个token如果不存在cookie中,就只有放在url上了,那么如果将生成的token串放在url中,就不能使用普通生成token的方式了,因为会生成一些如”+“这样的字符,造成异常。

  • 解决方案

    如果两个系统之间需要通过token认证,并且是通过url传输token的方式,那么生成token的工具类就要考虑到url的字符串不可识别域,主动避开,否则会出现字符串中的某些字符出现无法识别的情况,造成授权错误。

    例如abcd+aojdwbafa,如果作为token放在url上 ,由于url无法识别加号,这个串将会变成abcd adjdwbafa,中间的加号将会被省略,如果token存在redis中,就取不出来了,也就造成了认证错误。

    解决方法: 使用Apache的urlsafe来生成token

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /**
    * @Description: 令牌生成器
    * @Author: Object
    * @Date: 2019年12月6日
    */
    public class TokenGenUtil {
    private TokenGenUtil() {
    }
    private static TokenGenUtil instance;
    public synchronized static TokenGenUtil getInstance() {
    if(instance==null) {
    instance = new TokenGenUtil();
    }
    return instance;
    }
    public String generateTokeCode() throws NoSuchAlgorithmException {
    String value = System.currentTimeMillis() + new Random().nextInt() + "";
    // 获取数据指纹,指纹是唯一的
    MessageDigest md = MessageDigest.getInstance("md5");
    byte[] b = md.digest(value.getBytes());// 产生数据的指纹
    // Base64编码
    String to = Base64.encodeBase64URLSafeString(b);
    return to;// 制定一个编码
    }
    public static void main(String[] args) throws NoSuchAlgorithmException {
    System.out.println(TokenGenUtil.getInstance().generateTokeCode());
    }
    }

Echarts的响应式布局

  • 问题描述

    由于最近需要用到Echarts来制作数据可视化图表,但是在官网上学习的时候发现,初始化Echarts时需要有一个宽高固定的容器,这显然是非常不合适的。所以就自己写了一些js,让它可以根据容器宽度的变化自动调整。

  • 解决方案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    /******************Echart全局变量*********************/
    //如果不用全局变量,做响应式的时候会出现只有最后一个加载的图形可以响应式。
    //所以在暂时没有其它方法的情况下,先使用全局变量。
    //记住,每次增加Echarts图形都需要去resizeEchart()中写自适应信息
    var chart1 = {};
    var chart2 = {};
    var chart3 = {};
    var chart4 = {};
    /***************************************/
    function resizeEchart(){
    //Echarts注册区
    window.onresize = function(){
    //改变大小时重新生成
    setEchartOption(chart1);
    setEchartOption(chart2);
    setEchartOption(chart3);
    setEchartOption(chart4);
    }
    }
    //生成统计图
    function setEchartOption(chartObject){
    chartObject.chart = echarts.init(document.getElementById(chartObject.domId),'light');
    chartObject.chart.setOption(chartObject.option);
    var width = $("#"+chartObject.containerDomId).width();
    $("#"+chartObject.domId).width(width)
    chartObject.chart.resize();
    }

Shiro框架过滤器和SpringBoot冲突

  • 问题描述

    Shiro是现在为数不多的安全框架之一,其原理也是使用各式各样的过滤器进行权限控制,也支持自定义过滤器,但是在SpringBoot+Shiro的项目中自定义过滤器时会有冲突的问题。

  • 解决方案

    Shiro使用FilterChainDefinitionBean来定义过滤器链,定义的规则是先定义的先生效。那么如下代码:

    1
    2
    3
    4
    5
    LinkedHashMap<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
    filterChainDefinitionMap.put("/test","anon");
    filterChainDefinitionMap.put("/favicon.ico","anon");
    filterChainDefinitionMap.put("/**","url");
    return filterChainDefinitionMap;

    这段代码的意思应该是/test路径和/favicon.ico路径不进行拦截,而除此之外的路径都走url过滤器。 url过滤器是自定义的一个过滤器:

    1
    2
    3
    4
    5
    @Bean
    public URLPathMatchingFilter URLPathMatchingFilter() {
    URLPathMatchingFilter urlPathMatchingFilter = new URLPathMatchingFilter();
    return urlPathMatchingFilter;
    }

    但是如果这样定义过滤器相当于将过滤器交给Spring进行托管,直接的表现就是不管访问什么url都会通过这个过滤器,这明显不符合预期。 所以不能通过@Bean的方式配置这个自定义过滤器,应该将这个过滤器要交给Shiro管理,所以只需要将@Bean去掉,然后在ShiroFilter中调用这个方法来获取过滤器即可。

    1
    filters.put("url", URLPathMatchingFilter());

LayUI 删除数据后数据表格条数不减

  • 问题描述

    这可能是LayUI这个框架的一个Bug吧,在数据表格中对数据进行删除时,数据虽然会被删除掉,但是如果不进行手动刷新,数据表格下面的条数不会改变。

    自己想的一个解决方案就是,在删除数据之后进行reload,重新访问数据库进行数据获取,但是这样就增加了一次数据库IO,百度了一番发现别人也有遇到这个问题,自己也找到了一个行之有效的方法。

  • 解决方案

    1
    2
    data.del(); //删除对应行(tr)的DOM结构,并更新缓存
    $(".layui-laypage-btn").click();//删除后执行这一条语句,刷新

NginX HTTPS转HTTP请求

这不是踩坑,这是导师布置下来的一个任务,需要将外部https访问通过nginx转换为http。

  • 测试项目

    测试项目部署在8080端口上:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @RestController
    public class TestController {
    @GetMapping("/nginxTest")
    @ResponseBody
    public Map<String,Object> test(){
    Map<String,Object> modelMap = new HashMap<String,Object>();
    modelMap.put("测试nginx https->http", "true");
    return modelMap;
    }
    }
  • 生成证书

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [root@sdcode demo]# openssl genrsa -out privkey.pem 1024/2038  
    [root@sdcode demo]# openssl req -new -x509 -key privkey.pem -out server.pem -days 365
    Country Name (2 letter code) [XX]:CN
    State or Province Name (full name) []:xxxx
    Locality Name (eg, city) [Default City]:xxxx
    Organization Name (eg, company) [Default Company Ltd]:xxxx
    Organizational Unit Name (eg, section) []:jf
    Common Name (eg, your name or your server's hostname) []:ip地址或主机名
    Email Address []:610063090@qq.com
  • 配置NginX

    创建vhost文件夹,并编写conf文件

    1
    2
    3
    4
    5
    6
    7
    server {
    listen 80;
    server_name 192.168.0.249;
    location / {
    proxy_pass http://127.0.0.1:8080;
    }
    }

    将该conf文件导入nginx配置

    1
    include /usr/local/nginx/vhost/*.conf;

    配置HTTPS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # HTTPS server
    server {
    listen 443 ssl;
    server_name 192.168.0.249;
    #证书位置
    ssl_certificate /home/xxxx/demo/server.pem;
    ssl_certificate_key /home/xxxx/demo/privkey.pem;

    #ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;

    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    location / {
    proxy_pass http://192.168.0.249;
    }
    }

    重启nginx 报错: nginx: [emerg] the "ssl" parameter requires ngx_http_ssl_module in /usr/local/nginx/conf/nginx.conf:99 提示需要开启nginx的ssl模块,但是本环境没有安装。

    添加ssl模块:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #查看当前nginx信息
    [root@sdcode nginx-1.8.0]# /usr/local/nginx/sbin/nginx -V
    #输出
    nginx version: nginx/1.8.0
    built by gcc 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC)
    configure arguments: --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi
    #根据上述信息 在结尾添加 --with-http-ssl-module
    [root@sdcode nginx-1.8.0]# ./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --with-http_ssl_module

    #重新make
    [root@sdcode nginx-1.8.0]# make
    #将make成功的nginx覆盖原nginx
    [root@sdcode nginx-1.8.0]# cp ./objs/nginx /usr/local/nginx/sbin/
    #查看当前nginx信息
    [root@sdcode nginx-1.8.0]# /usr/local/nginx/sbin/nginx -V
    nginx version: nginx/1.8.0
    built by gcc 4.4.7 20120313 (Red Hat 4.4.7-23) (GCC)
    built with OpenSSL 1.0.1e-fips 11 Feb 2013
    TLS SNI support enabled
    configure arguments: --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --with-http_ssl_module
    #已经存在 ssl模块

Zuul丢失Cookie

  • 问题描述

    在一个SpringCloud项目中实现了一个认证授权中心,认证授权时需要获取到用户的cookie,但是通过zuul访问时,发现cookie丢失的情况。

  • 解决方案

    配置文件中加入

    1
    2
    #不过滤cookie
    sensitive-headers:

一些常见且易忘操作的记录

开启防火墙某端口:

1
firewall-cmd --zone=public --add-port=80/tcp --permanent

查看防火墙开启的端口:

1
firewall-cmd --list-ports

重启防火墙服务:

1
systemctl restart firewalld.service

MySQL被外键约束的表如何truncate

1
2
3
4
5
SET foreign_key_checks = 0;
truncate cloud_user;
truncate cloud_role;
truncate cloud_permission;
SET foreign_key_checks = 1;

Oracle创建用户并授权:

1
2
3
4
create user 用户名 identified by 密码; 
grant connect,resource to 用户名;
grant connect,resource,dba to 用户名;
revoke DBA from 用户名; --收回DBA

给自己的SpringMVC规范

如果是GET请求,且参数不复杂(两个以内),那么使用@RequestParam获取,也可以通过Restful来定义接口。/stu/{id},使用@PathVariable获取。

如果是GET请求,但是参数很多,那么使用HttpServletRequest获取,避免参数过于复杂。

如果是Post请求,且接收对象,那么就使用@RequestBody获取。

结语

你好2020。

原文作者:Object

原文链接:http://blog.objectspace.cn/2019/12/30/12月踩坑记录(2019)/

发表日期:2019 December 30th, 8:39:08 am

更新日期:2019 December 30th, 8:56:41 am

版权声明:未经作者授权请勿转载

目录
  1. 1. 前言
  2. 2. 连接Oracle数据库异常:ORA-12541:TNS无监听程序
    1. 2.1. 问题描述
    2. 2.2. 解决方案
  3. 3. MyBatis动态SQL字符串判等
    1. 3.1. 问题描述
    2. 3.2. 解决方案
  4. 4. MyBatis+Oracle使用日期作为条件查询
    1. 4.1. 问题描述
    2. 4.2. 解决方案
  5. 5. MyBatis一对多查询
    1. 5.1. 问题描述
    2. 5.2. 解决方案
  6. 6. JQuery的判空
    1. 6.1. 问题描述
    2. 6.2. 解决方案
  7. 7. Jedis存入Redis的数据,使用RedisTemplate无法取出
    1. 7.1. 问题描述
    2. 7.2. 解决方案
  8. 8. Jackson忽略属性
    1. 8.1. 问题描述
    2. 8.2. 解决方案
  9. 9. SpringBoot环境下实现过滤器
    1. 9.1. 问题描述
    2. 9.2. 解决方案
  10. 10. 父系统和子系统之间的单点授权登录
    1. 10.1. 问题描述
    2. 10.2. 解决方案
  11. 11. 生成url可以携带的token
    1. 11.1. 问题描述
    2. 11.2. 解决方案
  12. 12. Echarts的响应式布局
    1. 12.1. 问题描述
    2. 12.2. 解决方案
  13. 13. Shiro框架过滤器和SpringBoot冲突
    1. 13.1. 问题描述
    2. 13.2. 解决方案
  14. 14. LayUI 删除数据后数据表格条数不减
    1. 14.1. 问题描述
    2. 14.2. 解决方案
  15. 15. NginX HTTPS转HTTP请求
    1. 15.1. 测试项目
    2. 15.2. 生成证书
    3. 15.3. 配置NginX
  16. 16. Zuul丢失Cookie
    1. 16.1. 问题描述
    2. 16.2. 解决方案
  17. 17. 一些常见且易忘操作的记录
  18. 18. 给自己的SpringMVC规范
  19. 19. 结语