0%

明确需求

在使用Shiro的时候,鉴权失败一般都是返回一个错误页或者登录页给前端,特别是后台系统,这种模式用的特别多。但是现在的项目越来越多的趋向于使用前后端分离的方式开发,这时候就需要响应Json数据给前端了,前端再根据状态码做相应的操作。那么Shiro框架能不能在鉴权失败的时候直接返回Json数据呢?答案当然是可以。

其实Shiro的自定义过滤器功能特别强大,可以实现很多实用的功能,向前端返回Json数据自然不在话下。通常我们没有去关注它是因为Shiro内置的一下过滤器功能已经比较全了,后台系统的权限控制基本上只需要使用Shiro内置的一些过滤器就能实现了,此处再次贴上这个图。

这已经是我第三次贴这张图了

相关文档地址:http://shiro.apache.org/web.html#default-filters

我最近的一个项目是需要为手机APP提供功能接口,需要做用户登录,Session持久化以及Session共享,但不需要细粒度的权限控制。面对这个需求我第一个想到的就是集成Shiro了,Session的持久化及共享在Shiro系列第二篇已经讲过了,那么这篇顺便用一下Shiro中的自定义过滤器。因为不需要提供细粒度权限控制,只需要做登录鉴权,而且鉴权失败后需要向前端响应Json数据,那么使用自定义Filter再好不过了。

阅读全文 »

写这篇是因为最近将博客从服务器迁移到虚拟主机上了,我需要在Linux服务器上将我的Hexo博客编译后的静态页面上传至虚拟主机上,我的虚拟主机上传文件有且仅有一种方式:FTP

先说下我想实现的自动部署流程,我在Jenkins上做了监听,一旦我的Hexo代码仓库有新的提交,会触发任务脚本执行发布操作。具体要做的事都写在Shell脚本上,主要流程如下:

  1. 脚本配置,配置Hexo工作目录,静态文件上传目录,FTP配置信息等;
  2. 进入Hexo工作目录,更新代码,清除缓存,编译生成静态页面;
  3. 递归读取Hexo静态页面目录,创建文件夹/上传文件到FTP空间;
  4. 执行Hexo deploy命令,发布静态页面,我这里配置的是发布到Github;

流程大概就是这样,Shell脚本好说,FTP工具命令是踩了不少坑。说实话这FTP工具很不友好,一些常用的功能不支持,比如不能一次创建多级目录,只能一级一级逐层创建。

阅读全文 »

数据库优化是一个任重而道远的任务,想要做优化必须深入理解数据库的各种特性。在开发过程中我们经常会遇到一些原因很简单但造成的后果却很严重的疑难杂症,这类问题往往还不容易定位,排查费时费力最后发现是一个很小的疏忽造成的,又或者是因为不了解某个技术特性产生的。

于数据库层面,最常见的恐怕就是索引失效了,且一开始因为数据量小还不易被发现。但随着业务的拓展数据量的提升,性能问题慢慢的就体现出来了,处理不及时还很容易造成雪球效应,最终导致数据库卡死甚至瘫痪。造成索引失效的原因可能有很多种,相关技术博客已经有太多了,今天我要记录的是隐式转换造成的索引失效


数据准备

首先使用存储过程生成1000万条测试数据,
测试表一共建立了7个字段(包括主键),num1num2保存的是和ID一样的顺序数字,其中num2是字符串类型。
type1type2保存的都是主键对5的取模,目的是模拟实际应用中常用类似type类型的数据,但是type2是没有建立索引的。
str1str2都是保存了一个20位长度的随机字符串,str1不能为NULLstr2允许为NULL,相应的生成测试数据的时候我也会在str2字段生产少量NULL值(每100条数据产生一个NULL值)。

阅读全文 »

昨天遇到一个非常奇怪的问题,在一个Service中使用@Transactional注解的一个方法无论如何都不能开启事务。项目用的是Springboot和Mybatis Plus,权限验证用的是Shiro。Service层的伪代码如下:

1
2
3
4
5
6
7
8
9
@Transactional(rollbackFor = Exception.class)
public void register(String username, String password) {
Member member = new Member();
... ...
this.save(member);
MemberMessage memberMessage = new MemberMessage();
... ...
memberMessageService.save(memberMessage);
}

当memberMessage插入失败抛异常时,前面保存的member记录不会回滚。打断点发现,只要save(member)这行走完数据就直接插入,此时方法还没执行完,按道理事务应该还没提交,但是通过Navicat已经能够看到新增的记录了。怀疑是事务压根没开启,遂将logging.level.root日志等级改为DEBUG发现压根就没开启事务。

阅读全文 »

最近突然想整理一下我近十年来保存的资料(大部分是照片),然后就开始百度长期保存数据的最优解决方案,接着就在知乎看到这么一个主题:如何长时间保存重要数据?

先看到了最热门的一个回答,各种技术分析。完了我就想,最近是要多买个硬盘给我的资料做一下整理和备份了,防止发生意外数据丢失。
这个主题有很多知友参与解答,有通过各种专业知识进行分析的,也有抖机灵的,有一位知友引用《三体》的原文:

现代的量子存储器,就是那种一粒米大小可以放下一个大型图书馆的东西,里面的信息最多只能保存两千年左右,两千年后因为内部的什么衰变就不能读取了。其实这还是说那些质量最好的存储器,根据研究,现有的普通量子存储器,有三分之二在五百年内就会坏。

要论信息保存的时间,咱们那个时候的存储器还好些,他们找了些公元世纪的U盘和硬盘,有些居然还能读出来。据实验,这些存储器如果质量好,可以把信息保存五千年左右;特别是我们那时的光盘,如果用特殊金属材料制造,能可靠地保存信息十万年。但这些都不如印刷品,质量好的印刷品,用特殊的合成纸张和油墨,二十万年后仍能阅读。但这就到头了,就是说,我们通常用来存储信息的手段,最多只能把信息可靠地保存二十万年。

史前古陶器上的图案,保存了一万年左右;欧洲岩洞里发现的壁画,大约有四万年的历史;人类的人猿祖先为制造工具在石头上砸出的刻痕,如果也算信息的话,最早在上新世中期出现,距今约二百五十万年。可你别说,还真的找到了一亿年前留下来的信息,当然不是人类留下的,是恐龙的脚印。

“把字刻在石头上,可以保存一亿年。”

没错,信息保存时间最长的方式居然是最原始的石刻。但这些当然跟我现在想做的资料备份无关,我的数据最多也就保存到我这辈子结束为止。哪怕我儿子想怀念我,他会继续保存我留下来的资料作纪念,那我孙子呢,甚至子子孙孙呢,他们甚至在现实中都没见过我,又哪里会对我视若珍宝的资料感兴趣。是啊,那些记得你的人,爱你的人,跟你一起生活过的人,随着他们一个个都离开,我们就真正的从这个世界消失了,永远。。。

阅读全文 »

AbstractQueuedSynchronizer(抽象的队列式的同步器)简称AQS,AQS 中有一个非常重要的变量 state(同步状态,也可以理解为资源),所有与同步相关的操作都是跟它有关的。它是用 volatile 修饰的,volatile 保证了内存可见性但并不能保证并发操作时的原子性,所以除了常规的getset方法外还额外有一个 compareAndSetState()方法,这个方法最终调用了一个 Unsafe 类的本地CAS方法 compareAndSwapInt() 以保证操作的原子性。

1
2
3
4
5
6
7
8
9
10
11
12
13
private volatile int state;

protected final int getState() {
return state;
}

protected final void setState(int newState) {
state = newState;
}

protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
  • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false
  • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false
  • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false

ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,stateCAS1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。

一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire - tryReleasetryAcquireShared - tryReleaseShared中的一种即可。但AQS也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock

引用:https://www.cnblogs.com/waterystone/p/4920797.html

本篇主要来看看同步器独占模式下的工作原理,独占模式下我们重点关注acquire(int)release(int)这两个方法。

阅读全文 »

项目加入MongoDB支持,今天第一次用到,学新东西要做笔记。

MongoDB Version:3.4.6
spring-data-mongodb Version:1.10.23.RELEASE

org.springframework.data.mongodb.core.query.Criteria

CriteriaMongodb说明示例
Criteria and (String key)$and并且criteria.and("name")…
Criteria andOperator (Criteria… criteria)$and并且criteria.andOperator(Criteria.where("name")…)…
Criteria orOperator (Criteria… criteria)$or或者criteria.orOperator(Criteria.where("name")…)…
Criteria is (Object o)$is等于criteria.and("name").is("tom")
Criteria ne (Object o)$ne不等于criteria.and("name").ne("tom")
Criteria lt (Object o)$lt小于criteria.and("age").lt(20)
Criteria lte (Object o)$lte小等于criteria.and("age").lte(20)
Criteria gt (Object o)$gt大于criteria.and("age").gt(20)
Criteria gte (Object o)$gte大于等于criteria.and("age").gte(20)
Criteria in (Object… o)$in包含criteria.and("name").in("tom","jerry"…)
Criteria nin (Object… o)$nin不包含criteria.and("name").nin("tom","jerry"…)
Criteria regex(Pattern pattern)$regex模糊查询criteria.and("name").regex(\Pattern.compile("^.*t.*$"))
阅读全文 »

最近在写一个项目时需要通过反射获取方法的参数名,在method.getParameters()拿到的是一些arg0 arg1 arg2 …之类的无意义参数。

查原因发现是在Java8之前,代码编译为class文件后,方法参数的类型是固定的,但参数名称却丢失了,这和动态语言严重依赖参数名称形成了鲜明对比。(java是静态语言,所以入参名称叫什么其实无所谓的)。

阅读全文 »

其实关于Shiro的一些学习笔记很早就该写了,因为懒癌和拖延症晚期一直没有落实,直到今天公司的一个项目碰到了在集群环境的单点登录频繁掉线的问题,为了解决这个问题,Shiro相关的文档和教程没少翻。最后问题解决了,但我觉得我也是时候来做一波Shiro学习笔记了。

本篇是Shiro系列第四篇,Shiro中的过滤器初始化流程和实现原理。Shiro基于URL的权限控制是通过Filter实现的,本篇从我们注入的ShiroFilterFactoryBean开始入手,翻看源码追寻Shiro中的过滤器的实现原理。

阅读全文 »

其实关于Shiro的一些学习笔记很早就该写了,因为懒癌和拖延症晚期一直没有落实,直到今天公司的一个项目碰到了在集群环境的单点登录频繁掉线的问题,为了解决这个问题,Shiro相关的文档和教程没少翻。最后问题解决了,但我觉得我也是时候来做一波Shiro学习笔记了。

本篇是Shiro系列第三篇,Shiro中的过滤器初始化流程和实现原理。Shiro基于URL的权限控制是通过Filter实现的,本篇从我们注入的ShiroFilterFactoryBean开始入手,翻看源码追寻Shiro中的过滤器的实现原理。

阅读全文 »