分享

Spring Boot整合Shiro (使用shiro-spring-boot-web-starter)

 怡红公子0526 2021-05-26

Spring Boot 整合 Shiro 安全框架

同时整合 Druid 数据源、MyBatis 框架

整合 Druid 数据源是可选的

整合 MyBatis 框架只是为了登录页面展示数据库的数据

三个框架并没有依赖关系,按照任何顺序整合都可以,也可以只整合Shiro

此章主要是关于 Shiro 的整合摸索,所以我将首先整合 Shiro

完整代码和更多的文档请移步Github springboot-05-shiro

建议对照观看


Shiro 的基本功能和概念

此部分对应 shiro-quickstart 模块

这是一个单独的 Maven 项目,本质上就是 Shiro 官网的10分钟快速上手

主要参考代码是Github 样例

也参考Shiro用starter方式优雅整合到SpringBoot中

因为 Shiro 属于开源项目,所以文档的维护并不是很及时与准确

一定要以最新版的源码为准

必须了解的关于安全的概念

这两个概念不理解,后面Shiro的授权一定会云里雾里

  • Authentication:认证、鉴权
  • Authorization:授权

与Spring Boot整合

值得一提的是,目前网上的很多整合教程都是导入shiro-spring

我们知道,这个shiro-spring包本质上只是和Spring整合的包,导入之后我们还需要写一个@Configuration用以和Spring Boot整合

事实上,已经有shiro-spring-boot-web-starter包发布,就像mybatis-spring-boot-starter一样,它已经帮助我们完成了部分配置

导入之后我们还需要进行少量的配置就可以了

需要我们配置的部分

由于安全策略与具体的业务会有联系,就比如说不同的项目所含有的角色和权限定义是完全不同的,所以这部分必定是我们自己配置

并不存在完全自动配置的情况

参考 官网对Shiro和Spring Boot应用整合的说明

目前存在两者需要我们配置的部分

Realm

这个词的中文翻译是领域,Shiro在 前面的文档 部分也对其做了解释

我个人的理解是由于不同业务被认证和授权的主体不同,这里就是要定义这个主体是什么,以及如何对它进行授权和认证

Shiro 为我们提供了现成的Realm可以直接使用,本样例中我使用类全限定名是org.apache.shiro.realm.jdbc.JdbcRealm

也就是从数据库读取用户的信息 (用户名、密码、角色、权限),然后进行认证

JdbcRealm中定义了默认的查询语句,我们根据查询语句建立对应的数据库表

protected static final String DEFAULT_AUTHENTICATION_QUERY="select password from users where username = ?";

protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY="select password, password_salt from users where username = ?";

protected static final String DEFAULT_USER_ROLES_QUERY="select role_name from user_roles where username = ?";

protected static final String DEFAULT_PERMISSIONS_QUERY="select permission from roles_permissions where role_name = ?";

当然,JdbcRealm是支持覆写SQL语句的,你可以用set***Query方法覆写内置的SQL语句

不过这里对SQL语句查询结果应该还会有其他要求,具体的我还没有研究,我使用的就是默认的

同时还需要设置一个数据源

private DataSource dataSource;

@Autowired
public void setDataSource(DataSource dataSource){
    this.dataSource=dataSource;
}

@Bean
public Realm realm(){
    JdbcRealm realm=new JdbcRealm();
    realm.setDataSource(dataSource);
    // 基于权限的资源访问默认是关闭的
    realm.setPermissionsLookupEnabled(true);
    return realm;
}

那么由于我使用的是自带提供的JdbcRealm,所以认证、授权的过程都不用我来具体实现,其还支持加盐功能

ShiroFilterChainDefinition

定义完进行认证和授权的 Subject,下一步就是定义应该按照什么样的规则进行认证和授权

官网关于认证的文档

官网关于授权的文档

Shiro框架支持通过URL或者注解配置认证和授权,这两种方式没有好坏之分,参考网上的说法,往往两种结合才更有效

关于过滤器可以参考默认过滤器

这里贴出我写的过滤规则

@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition(){
    DefaultShiroFilterChainDefinition chainDefinition=new DefaultShiroFilterChainDefinition();
    // 登出功能
    chainDefinition.addPathDefinition("/logout","logout");
    // 错误页面无需认证
    chainDefinition.addPathDefinition("/error","anon");
    // druid连接池的角色控制,只有拥有admin角色的admin用户可以访问,不理解可以先不管
    chainDefinition.addPathDefinition("/druid/**","authc, roles[admin]");
    // 静态资源无需认证
    chainDefinition.addPathDefinition("/static/**","anon");
    // 其余资源都需要认证
    chainDefinition.addPathDefinition("/**","authc");
    return chainDefinition;
}

大体思想是在URL中配置认证规则 (鉴权),在Controller中使用注解配置授权,这样的优势:

  1. 减少代码量,不必为每个Controller都写上鉴权规则
  2. 利用注解可以细粒度设置基于角色或者基于资源的权限

基于角色的访问控制

现在我们来看这一条规则

chainDefinition.addPathDefinition("/druid/**","authc, roles[admin]");

由于在配置完Shiro后,我为项目整合了 Druid
数据源,如果你对它不是很了解,建议去查看Github 文档

这里无需对 Druid 有很详细的了解,我想阐述的最关键的一点是,当你整合成功 Druid 框架,并且开启了 Web 统计功能 (参考我的application.yml)

访问你的 项目URL/druid 会进入 Druid 数据源的管理界面,这里需要输入你之前设置的用户名和密码才能查看项目对每一条执行过的SQL的统计情况

显然这个功能我不希望普通角色的用户也能查看,所以我将其设置为所有匹配规则 /druid/** 的URL都需要登录并且需要有admin角色

对应的,你可以尝试使用admin用户去登录并访问/druid页面,我已经将admin用户设置为admin角色 (这里可能有一些绕,原谅我是为了省事才把两个名字设置相同的,这里的用户名完全可以修改为你自己的名字!)

之后,再尝试使用andy67123用户去登录并访问/druid页面,它的角色仅仅是访客,看看会有什么不同

类似的,还有一处使用注解来控制角色访问

AccountInfoController中,有这么一段代码表明这处的资源需要admin角色才能访问

@RequiresRoles("admin")
@RequestMapping("/account-info")
public String accountInfoTemplate(Model model){
    ...
}

基于权限的访问控制

最后,我们来聊Shiro中常用功能同样也很重要的一部分——Permissions

先上官方文档 Understanding Permissions in Apache Shiro

这部分我也是想了很久,什么时候需要使用基于角色的访问控制,什么时候需要使用基于资源的访问控制,什么时候可以两者合并使用

诚然,你的系统完全可以只使用基于角色的访问控制,在此样例中就只需要usersuser_roles两张表,然后在Controller使用@RequiresRoles("role")注解去指定角色

也可以只使用基于权限的访问控制 (依然需要角色,但是不再通过角色控制访问),在此样例中需要usersuser_rolesroles_permissions
三张表,然后在Controller使用@RequiresPermissions("perm")注解去指定权限

这一部分关于如何设计以及谁优谁劣先暂且不讨论,因为我也不知道

此处先把 Shiro 的 Permissions 样例展示一下

首先查看角色和权限对应的关系

# 角色 权限
1 admin manage:*
2 访客 read:*
3 java开发 read:*
4 java开发 dev:java
5 python开发 read:*
6 python开发 dev:python

也可以参考进入的/login页面提供的信息

  • admin角色拥有所有的管理权限
  • 访客只有查看文档的权限
  • java开发可以查看文档和进行java代码开发
  • python开发可以查看文档和进行python代码开发

再查看用户和角色对应的关系

# 用户 角色
1 admin admin
2 andy67123 访客
3 admin java开发
4 admin python开发
5 java java开发
6 python python开发
  • admin用户拥有adminjava开发python开发三种角色
  • andy67123用户拥有访客角色
  • java用户拥有java开发角色
  • python用户拥有python开发角色

请试一试登录完成后,在主页点击各个按钮的结果,是否实现了各资源的权限控制

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多