当前位置:首页 > 问答 > 正文

Shiro安全框架里数据库配置那些事儿,细节和坑都得知道才行

首先得明白,Shiro本身不管理数据库连接,它关心的是如何从你的数据库里拿到安全数据,比如用户信息、角色和权限,这里说的“数据库配置”,核心是告诉Shiro如何去你的数据库里“查”这些东西,这事儿主要在和Shiro的核心SecurityManager打交道时完成,特别是配置它的Realm

第一个大坑:Realm的选择与实现

Shiro通过Realm这个桥梁来连接安全数据源,虽然Shiro提供了一些现成的JDBC Realm,但根据网络上的大量开发者经验(例如来自CSDN、博客园等平台的分享),直接使用Shiro自带的JdbcRealm往往不够灵活,容易踩坑,它预设了一些固定的SQL查询语句,比如select password from users where username = ?,如果你的数据库表结构或者命名规则跟它的预设不一样,就得想办法去覆盖这些SQL,过程很别扭。

Shiro安全框架里数据库配置那些事儿,细节和坑都得知道才行

更普遍、更受推荐的做法是自己写一个Realm,继承AuthorizingRealm这个类,这样做的好处是,控制权完全在你手里,你需要重写两个关键方法:

  1. doGetAuthenticationInfo:负责认证,你是谁?”的问题,这个方法里,你需要根据用户名(比如从登录表单来的)去数据库找到对应的用户信息,包括加密后的密码和盐值,然后返回给Shiro,Shiro会拿这个和你提交的密码进行加密比对。
  2. doGetAuthorizationInfo:负责授权,你能干什么?”的问题,在用户登录成功后,Shiro会调用这个方法,去数据库查询这个用户拥有的所有角色和权限,然后封装起来。

第二个细节:密码比对与加密

这是安全的重中之重,也是容易出问题的地方,在doGetAuthenticationInfo方法里,你不能直接把数据库里存的密码明文返回,Shiro的认证逻辑是:它会把用户登录时提交的明文密码,用你指定的方式加密,然后跟数据库里存储的加密密码进行比对。

Shiro安全框架里数据库配置那些事儿,细节和坑都得知道才行

这里的关键配置是CredentialsMatcher(凭证匹配器),你必须显式地为你自定义的Realm设置一个匹配器,常用的有:

  • HashedCredentialsMatcher:这是最常用的,用于处理MD5、SHA1等哈希加密的密码。
  • 你需要设置哈希算法名称(如md5)、哈希迭代次数(如1024)等属性,这里有个非常重要的细节:如果你的密码加密时加了“盐”(salt),一定要在doGetAuthenticationInfo方法里,把从数据库查出来的盐值也一并设置到返回的AuthenticationInfo对象里,Shiro的匹配器会使用相同的盐值对用户输入的密码进行加密,这样才能正确比对,忘记设置盐值是一个常见的导致登录失败的坑。

第三个坑:SQL查询与数据模型设计

当你自己写Realm时,执行什么样的SQL查询就完全由你决定了,这里有几个需要注意的点:

Shiro安全框架里数据库配置那些事儿,细节和坑都得知道才行

  • 关联查询:用户、角色、权限之间通常是多对多的关系,你需要在doGetAuthorizationInfo方法里,通过联表查询(比如users表联user_roles表再联roles表,roles表再联role_permissions表)一次性把用户的所有权限信息查出来,避免为每个权限都发起一次数据库查询,那会造成著名的“N+1查询问题”,严重拖慢性能,很多性能问题都出在这里。
  • 缓存:正因为授权信息查询可能会涉及多表关联,为了减轻数据库压力,一定要为授权信息配置缓存,Shiro支持集成EhCache、Redis等流行的缓存框架,你可以在Realm上设置authorizationCacheName等属性,并配置好对应的缓存管理器(CacheManager),这样,用户第一次访问时查询数据库,之后一段时间内直接从缓存读取权限信息,效率大大提升,但要注意缓存失效的问题,比如用户权限变更后需要及时清除缓存。
  • 数据一致性:你的数据库表结构设计要合理,最起码要有用户表、角色表、权限表,以及它们之间的关联表,字段名要清晰,比如密码字段、盐值字段、状态字段(用于标记用户是否被禁用)等。

第四个细节:整合到Shiro配置(INI或Spring)

你需要把上面这些组件像搭积木一样组装起来,配置给Shiro的SecurityManager,无论你是用传统的shiro.ini文件还是Spring/Spring Boot框架,思路都是一样的。

  1. 配置数据源:首先得有一个数据源(DataSource),比如使用DBCP、C3P0或者HikariCP连接池,这个数据源会在你自定义的Realm的DAO层被使用。
  2. 配置Realm:实例化你的自定义Realm,并为其注入数据源(或者你的MyBatis Mapper、JPA Repository等),并设置好之前提到的CredentialsMatcher及其相关属性(如哈希算法、迭代次数)。
  3. 配置SecurityManager:将配置好的Realm设置给SecurityManager,如果使用了缓存,还需要配置CacheManager并设置给SecurityManager和Realm。

总结一下最容易遇到的坑:

  1. 登录总是失败:八成是密码比对出了问题,检查加密算法、迭代次数是否与注册时一致;检查盐值是否正确地从数据库取出并设置。
  2. 权限不生效:检查doGetAuthorizationInfo方法里的SQL是否正确查询到了权限数据;检查是否配置了缓存但缓存中有脏数据。
  3. 性能瓶颈:检查授权查询SQL是否优化过,是否避免了N+1查询;确认已经正确配置了缓存。

Shiro的数据库配置核心在于“定制”,理解了Realm的工作原理、密码比对的流程、以及如何高效地查询和缓存安全数据,就能避开大部分陷阱,让Shiro稳固地守护你的应用安全。