一、概念

什么是shiro

shiro是基于java的开源的安全管理框架。可以完成认证,授权,会话管理,加密,缓存等功能。

Primary Concerns

Authentication:认证

验证用户是否合法。也就是登陆。

Authorization:授权

授予谁具有访问某些资源的权限。

Session Management:会话管理

用户登录后的用户信息通过Session Management来进行管理,不管是在上面应用中。

Cryptograpy:加密

提供了常见的一些加密算法,使得在应用中很方便的实现数据安全,并且使用很便捷。

Supporting Features

Web support:web应用程序支持

​ Shiro可以很方便的集成到web应用程序中。

Caching:缓存

​ Shiro提供了对缓存的支持,多种缓存架构:如ehcache,还支持缓存数据库:如redis

Concurrency:并发支持

​ 支持多线程并发访问

Testing:测试

RunAs:一个允许用户假设为另一个用户身份(如果允许)的功能。

Remember Me:记住我

Shiro的架构

Subject(org.apache.shiro.subject.Subject)

​ 当前与软件进行交互的实体(用户,第三方服务,cron job,等等)的安全特定“视图”。

SecurityManager(org.apache.shiro.mgt.SecurityManager):安全管理器

​ 如上所述,SecurityManager是Shiro架构的心脏。它基本上是一个“保护伞”对象,协调其管理的组件以确保它们能够一起顺利的工作。它还管理每个应用程序用户的shiro的视图,因此它知道如何执行每个用户的安全操作

Authenticator(org.apache.shiro.authc.Authenticator):认证器

​ 负责验证用户的身份

Authorizer:授权器

​ 负责为合法的用户指定其权限。控制用户可以访问那些资源。

Realms:域

用户通过shiro来完成相关的安全工作,shiro是不会维护数据信息的。在shiro的工作过程中,数据的查询和获取工作是通过Realm从不同的数据源来获取的。Realm可以获取数据库信息,文本信息等。

二、用户认证

Authentication:用户认证

验证用户是否合法,需要提交身份和凭证给shiro。

principals:用户的身份信息,是subject的标识属性。能够唯一标识Subject。如:电话号码,电子邮箱,身份证号码等。

Credentials 凭证:密码。是只被subject知道的秘密值。可以是密码,也可以是数字证书等。

Pricipals/Credentials 最常见的组合:用户名/密码。在shiro中通常使用UsernamePasswordToken来指定身份和凭证信息。

在shiro中用户的认证流程

1、subject -> 2、sucurity manager ->3、Authenticator ->4、Authentication-> 5、JDBC Realm

shiro.ini

1
2
3
[users]
zhangsan=1111
lisi=1111

AuthenticationDemo

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
public class AuthenticationDemo {
public static void main(String[] args) {
//1、创建SecurityManager工厂 读取相应的配置文件
Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2、通过SecurityManager工厂获取SecurityManager的实例
SecurityManager securityManager = factory.getInstance();
//3、将SecurityManager对象 设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//4、通过SecurityUtils获取主体Subject
Subject subject = SecurityUtils.getSubject();
try {
//5、加入登录的用户名zhangsan和1111,
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "1111");
//进行用户身份验证
subject.login(token);
//通过subject判断用户是否通过验证
if (subject.isAuthenticated()) {
System.out.println("用户登录成功");
} else {
System.out.println("用户名或密码不正确");
}
}catch (AuthenticationException e) {
e.printStackTrace();
}
}
}

常见的异常信息及处理

在认证过程中有一个父异常为:AuthenticationException。

该异常有几个子类:

  • DisabledAccountException 账户失效异常

  • ExcessiveAttemptsException 尝试次数过多

  • UnknowAccountException 用户不正确
  • ExpiredCredentialsException 凭证过期异常
  • IncorrrectCredentialsException 凭证不正确

执行流程

  • 通过shiro相关api,创建securityManager及获取Subject实例
  • 封装token信息
  • 通过subject.login(token)进行用户认证
    • Subject接收token,通过其实现类DelegatingSubjecttoken委托给SecurityManager来完成认证。SecurityManager是接口通过DefaultSecurityManager来完成相关的功能。由DefaultSecurityManagerlogin来完成认证过程。在login调用了该类authenticate()来完成认证。该方法是由AuthenticatingSecurityManager来完成的。在该类的authenticate()中,通过调用authenticator(认证器)来完成认证工作。Authenticator是由其默认实现类ModularRealmAuthentucator来完成认证。通过ModularRealmAuthentucator中的doAuthenticate来获取Realms信息。如果是单realm直接将token和realm中的数据进行比较,判断是否认证成功。如果是多realm那么需要通过Authentication Strategy来判断对应的认证工作。
  • 通过subject.isAuthenticate()来判断是否认证成功。

依赖包

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-aspectjartifactId>
<version>1.3.2version>
dependency>

<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-casartifactId>
<version>1.3.2version>
dependency>

<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.3.2version>
dependency>

<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-ehcacheartifactId>
<version>1.3.2version>
dependency>

<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-guiceartifactId>
<version>1.3.2version>
dependency>

<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-quartzartifactId>
<version>1.3.2version>
dependency>

<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.3.2version>
dependency>

<dependency>
<groupId>org.apache.shiro.toolsgroupId>
<artifactId>shiro-tools-hasherartifactId>
<version>1.3.2version>
dependency>

<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-webartifactId>
<version>1.3.2version>
dependency>

<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.12version>
<scope>testscope>
dependency>

<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.21version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>

三、JDBCRealm及Authentication Strategy

1、使用shiro框架来完成认证工作,默认情况下使用的是IniRealm。如果需要使用其他Realm,name需要进行相关的配置。

2、Ini配置文件:

[main] section是配置应用程序SecurityManager实例及任何它的依赖组件(如Realms)的地方

1
2
3
4
[main]
myRealm=cn.sxt.realm.MyRealm
# 依赖注入
securityManager.reakm=$myRealm

[users]section允许定义一组静态的用户账户。在这大部分拥有少数用户账户或用户账户不需要在运行时被动地创建的环境下是很有用的。

1
2
3
[users]
Zhangsan=1111
Lisi=2222,role1,role2

[roles]section允许把定义在[user]section中的角色与权限关联起来。另外,这在大部分拥有少数用户账户或账户或用户账户不需要在运行时被动态地创建的环境下是很有用的。

1
2
3
4
5
[users]
Zhangsan=1111
Lisi=2222,role1,role2
[roles]
role1=user:add,user:delete

3.使用JdbcRealm来完成身份认证

需要实现JdbcRealm

  • 需要为JdbcRealm设置dataSource
  • 在指定的dataSource所对应的数据库中应用表users,该表中有username,password,password_salt等字段

4.实现步骤:

  • 新建数据库表
  • 配置shiro.ini文件
  • 编写认证

5.Authentication Strategy:认证策略,在shiro中有三种认证策略;

  • AtLeastOneSuccessfulStrategy:如果一个(或更多Realm)验证成功,则整体尝试被认为是成功的。如果没有一个成功,则整体尝试失败。
  • 第一个成功则被认为成功
  • 所有成功被认为成功

四、MD5加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Md5Demo {
public static void main(String[] args) {
//使用md5加密算法加密
Md5Hash md5 = new Md5Hash("1111");
System.out.println("1111=="+md5.toString());
//加盐
md5 = new Md5Hash("1111", "sxt");
System.out.println("1111=="+md5.toString());
//迭代次数
md5 = new Md5Hash("1111", "sxt",2);
System.out.println("1111=="+md5.toString());
SimpleHash hash = new SimpleHash("md5", "1111", "sxt", 2);
System.out.println(hash.toString());
}
}
1
2
3
4
1111==b59c67bf196a4758191e42f76670ceba
1111==96c0335dbdd59d920980f1c6a74ed1b0
1111==e41cd85110c7533e3f93b729b25235c3
e41cd85110c7533e3f93b729b25235c3

六、授权

1、授权:给身份认证通过的人,授予他可以访问某些资源的权限。

2、权限粒度:分为粗粒度和细粒度。

  • 粗粒度:对user的crud操作。

  • 细粒度:对记录的操作。如:值允许查询id为1的user的工资。

  • shiro一般管理的是粗粒度。如:菜单、按钮、url。一般细粒度的权限是通过业务来管理的。

3、角色:权限的集合。

4、权限表示规则:资源:操作:实例。可以用通配符表示:

  • 如:user:add 表示对user有添加的权限。user:* 表示对user具有所有操作的权限
  • user:delete:100表示对user标识为100的记录有删除的权限。

5、shiro中的权限流程:

image-20191230173141540
image-20191230173141540

七、Shiro中的权限检查方式有3种

  • 编程式
1
2
3
if(subject.hasRole("管理员")) {
//操作某个资源
}
  • 注解式
1
2
3
4
@RequireRole
public void list(){
//查询数据
}
  • 标签

    1
    2
    3
    "user.update">
    更新

八、授权流程:

  • 获取subject主体

  • 判断主体是否通过认证

  • 调用subject.isPermitted*/hasRole*来进行权限的判断

    • Subject是由其实现类DelegatingSubject来调用方法的,该类将处理交给了SecurityManager
    • SecurityManager是由其实现类DefaultSecurityManager来进行处理,该类的isPermitted来处理,其本质父类AuthorizingSecurityManager来处理的。该类将 处理交给了authorizer(授权器)
    • Authorizer由其实现类ModularRealmAuthorizer来处理该类可以调用对应的Realm来获取数据,在该类有PermissionResolver对权限字符串进行解析,在对应的Realm中也有对应的PermissionResolver交给WildcardPermissionResolver该类调用WildcardPermission来进行权限字符串的解析
    • 返回处理结果

    九、自定义Realm实现授权

    1、仅仅通过配置文件来指定权限不够灵活,并且不方便。在实际的应用中大多数情况下都是将用户信息,角色信息,权限信息保存到了数据库中。所以需要从数据库中去获取相关的数据信息。可以使用shiro提供的JdbcRealm来实现,也可以自定义realm来实现。使用jdbcRealm往往不够灵活,