SpringBoot + Shiro + shiro.ini 的踩坑记录

2023-01-01,,,

0、写在前面的话

好久没写博客了,诶,好多时候偷懒直接就抓网上的资料丢笔记里了,也就没有自己提炼,偷懒偷懒。然后最近参加了一个网络课程,要交作业的那种,为了能方便看下其他同学的作业,就写了个爬虫把作业爬下来,进而想到如果以后还有类似这种情况,我需要一个非常轻量化的架子,但是权限依然是必须要用到的,否则写个接口人人都可以调用那还得了,但是我仅仅需要一个人或者一两个人的权限配置就可以了,所以像之前我写过一个基于RBAC的架子甚至都还是有点嫌重(而且那个架子还没有前端页面,哈,真是懒),所以想到了用shiro.ini

Shiro在很多quick start的demo中都用到了shiro.ini配置文件,用来配置账户密码、角色、权限,它也写好了现成的方法可以读取文件直接给你配置好SecurityManager,所以省去了自己在代码里定义Realm、配置过滤器啥的,但是实际上大家在做Web时基本都不用这玩意儿,因为自定义其实也不是很费时,我也就是蛋疼吧,既然都鼓捣了,那就还是鼓捣到有个雏形。

1、踩坑记录

Shiro好久没碰了,又重新回顾了下大概几个概念:

SecurityManager是核心,所有和安全有关的操作都要和它交互,而且管理者所有Subject
Realm用来验证用户,可以理解为DataSource安全数据源
权限拦截是通过过滤器,配置好urls和shiro默认过滤器的映射关系,shiro将会对requestUrl进行过滤链的匹配,并选择过滤器进行处理

[users]
zhang=123,admin
wang=123,admin,vip [roles]
admin=user:delete [urls]
/static/**=anon
/login=anon
/authc/admin/user/delete=perms["user:delete"]
/authc/admin/user/create=perms["user:create"]
/authc/admin/**=roles[admin]
/authc/home=roles[admin,vip]
/authc/**=authc

x

17

 

1

[users]

2

zhang=123,admin

3

wang=123,admin,vip

4


5

[roles]

6

admin=user:delete

7


8


9

[urls]

10

/static/**=anon

11

/login=anon

12

/authc/admin/user/delete=perms["user:delete"]

13

/authc/admin/user/create=perms["user:create"]

14

/authc/admin/**=roles[admin]

15

/authc/home=roles[admin,vip]

16

/authc/**=authc

shiro.ini中的 [main] 用不上了,因为这部分的配置和SpringBoot结合就在类中进行配置,这里就不需要了,只需要 [users]、[roles]、[urls],其中[users] 和 [roles] 用来创建 Realm,[urls] 用来配置过滤器

踩坑:

urls过滤匹配中,是按顺序执行匹配到的第一个过滤器,所以要注意顺序,如上图如果把 /authc/** 放在开头,后面的基本就匹配不到了
urls中对于诸如perms或roles的描述,是“且”不是“或”
比如 roles[admin,vip] 表示同时拥有admin和vip角色的账户,而不是拥有admin或vip角色的账户
* 匹配零个或多个字符    ** 匹配零个或多个路径

@Configuration
public class ShiroConfig { @Bean
public DefaultWebSecurityManager securityManager() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance(); DefaultSecurityManager defaultSecurityManager = (DefaultSecurityManager) securityManager;
DefaultWebSecurityManager webSecurityManager = new DefaultWebSecurityManager();
webSecurityManager.setRealms(defaultSecurityManager.getRealms()); //important SecurityUtils.setSecurityManager(securityManager);
return webSecurityManager;
} @Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager); Ini ini = new Ini();
ini.loadFromPath("classpath:shiro.ini");
Map<String, String> map = new LinkedHashMap<>(); ini.getSection("urls").entrySet().forEach(url -> {
map.put(url.getKey(), url.getValue());
});
//过滤链
shiroFilterFactoryBean.setFilterChainDefinitionMap(map); shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
return shiroFilterFactoryBean;
} }
x

 

1

@Configuration

2

public class ShiroConfig {

3


4

    @Bean

5

    public DefaultWebSecurityManager securityManager() {

6

        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

7

        SecurityManager securityManager = factory.getInstance();

8


9

        DefaultSecurityManager defaultSecurityManager = (DefaultSecurityManager) securityManager;

10

        DefaultWebSecurityManager webSecurityManager = new DefaultWebSecurityManager();

11

        webSecurityManager.setRealms(defaultSecurityManager.getRealms()); //important

12


13

        SecurityUtils.setSecurityManager(securityManager);

14

        return webSecurityManager;

15

    }

16


17

    @Bean

18

    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {

19

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

20

        shiroFilterFactoryBean.setSecurityManager(securityManager); 

21


22

        Ini ini = new Ini();

23

        ini.loadFromPath("classpath:shiro.ini");

24

        Map<String, String> map = new LinkedHashMap<>();

25


26

        ini.getSection("urls").entrySet().forEach(url -> {

27

            map.put(url.getKey(), url.getValue());

28

        });

29

        //过滤链

30

        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

31


32

        shiroFilterFactoryBean.setLoginUrl("/login");

33

        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

34

        return shiroFilterFactoryBean;

35

    }

36


37

}

踩坑

SecurityManager是一定要有的,但是Shiro中读取shiro返回的是 DefaultSecurityManager,因为是Web应用我们需要的是 DefaultWebSecurityManager,所以把 DefaultSecurityManager的Realms 提出来给 DefaultWebSecurityManager
过滤链还是得注入在Bean中的 FilterChainDefinitionMap 属性才是,所以对于shiro.ini的配置,使用 Ini 类的 loadFromPath 来读取,再放置到map中

...
<packaging>war</packaging>
... ...
<!-- jsp支持 start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency> <dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!-- jsp支持 end -->
...

1

12

 

1

...

2

<packaging>war</packaging>

3

...

4


5

...

6

<!-- jsp支持 start -->

7

<dependency>

8

    <groupId>org.springframework.boot</groupId>

9

    <artifactId>spring-boot-starter-tomcat</artifactId>

10

    <scope>provided</scope>

11

</dependency>

12


13

<dependency>

14

    <groupId>org.apache.tomcat.embed</groupId>

15

    <artifactId>tomcat-embed-jasper</artifactId>

16

</dependency>

17

<!-- jsp支持 end -->

18

...
#application.properties
spring.mvc.view.prefix=/WEB-INF/pages/
spring.mvc.view.suffix=.jsp

1

 

1

#application.properties

2

spring.mvc.view.prefix=/WEB-INF/pages/

3

spring.mvc.view.suffix=.jsp

SpringBoot 对 JSP 的支持并不友好,所以需要一些额外的配置,参考资料《Spring Boot 添加 JSP 支持》

SpringBoot + Shiro + shiro.ini 的踩坑记录的相关教程结束。

《SpringBoot + Shiro + shiro.ini 的踩坑记录.doc》

下载本文的Word格式文档,以方便收藏与打印。