shiro权限管理
Shiro的核心就是用来完成权限管理的。权限管理包括用户身份认证和授权两部分,简称认证授权。
只要有人参与的系统都需要进行权限管理。
什么是Shiro?
Shiro是apache旗下的一个开源框架,它将软件系统的安全认认证相关的功能抽取出来,实现用户身份认证、权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。

- **Security Manager:**包含Authenticator认证器,Authorizer授权器,Session Manager会话管理器,Cache Manager缓存管理器,Session DAO会话数据访问对象,Pluggable Realms可插拔域
- **cryptography:**密码器(sha256,md5)
- **subject:**主体(Shiro集成的东西,宿主)
Shiro中用户的身份认证
认证流程:

代码
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
|
[users]
zhangsan=123
lisi=1234
wangwu=12345
zhaoliu=123456
public class TestAuhenticator
{
public static void main( String[] args )
{
//创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//SecurityUtils安全工具类
SecurityUtils.setSecurityManager(securityManager);
//拿到主体subject
Subject subject = SecurityUtils.getSubject();
//模拟登录,创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
//用户登录
try {
System.out.println("Authentication Status:"+subject.isAuthenticated());
subject.login(token);
System.out.println("Authentication Status:"+subject.isAuthenticated());
}
catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("认证失败,用户名不存在!");
}
catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("认证失败,密码错误!");
}
}
}
|
源码剖析
-
最终执行用户名校验,在 SimpleAccountRealm类(继承于AuthorizingRealm) doGetAuthenticationInfo() 这个方法中完成用户名校验:查看是否有这个用户
-
最终密码的校验,在 AuthenticatingRealm类 assertCredentialsMath() 这个方法中完成密码校验的
-
通过剖析源码可知,如果要把Realm放在数据库中,只需要继承AuthorizingRealm类,然后重写 doGetAuthenticationInfo() 方法即可
-
几个重要Realm类的继承关系图:

-
总结:
AuthenticatingRealm中有认证方法:doGetAuthenticationInfo()
AuthorizingRealm中有授权方法:doGetAuthorizationInfo()
所以,当实际上操作数据库时,我们自定义的Realm类实际上是继承AuthorizingRealm类,然后重写 doGetAuthenticationInfo() 和 doGetAuthorizationInfo() 两个方法。
自定义Realm
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
|
public class CustomRealm extends AuthorizingRealm {
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String principal = (String) authenticationToken.getPrincipal();
String credentials = new String((char[]) authenticationToken.getCredentials());
System.out.println("************************************");
System.out.println(principal);
System.out.println(credentials);
if("zhangsan".equals(principal)){
return new SimpleAuthenticationInfo("zhangsan","123",this.getName());
}
return null;
}
}
public class TestAuhenticator
{
public static void main( String[] args )
{
//创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//给安全管理器设置realm
// securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
securityManager.setRealm(new CustomRealm());
//SecurityUtils安全工具类
SecurityUtils.setSecurityManager(securityManager);
//拿到主体subject
Subject subject = SecurityUtils.getSubject();
//模拟登录,创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
//用户登录
try {
System.out.println("Authentication Status:"+subject.isAuthenticated());
subject.login(token);
System.out.println("Authentication Status:"+subject.isAuthenticated());
}
catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("认证失败,用户名不存在!");
}
catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("认证失败,密码错误!");
}
}
}
|
密码加密
MD5算法
- 一般用来加密 & 签名
- MD5加密不可逆
- MD5算法生成结果始终是一个16进、32位长度的字符串
- 一般在后端代码的业务层完成加密操作如果要进行用户名
- 使用MD5算法,输入只会有唯一输出。所以为了防止有人恶意穷举试探密码,我们需要给密码加盐后再MD5机密
Shiro中的MD5加密
直接看代码
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
|
public class CustomRealm extends AuthorizingRealm {
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String principal = (String) authenticationToken.getPrincipal();
String credentials = new String((char[]) authenticationToken.getCredentials());
System.out.println("************************************");
System.out.println(principal);
System.out.println(credentials);
if("zhangsan".equals(principal)){
return new SimpleAuthenticationInfo("zhangsan","123", ByteSource.Util.bytes("abc*"),this.getName());
}
return null;
}
}
public class TestAuhenticator
{
public static void main( String[] args )
{
//创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//新建自定义realm对象
CustomRealm realm = new CustomRealm();
//设置realm使用hash凭证检测器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(1024);//设置加盐散列次数
realm.setCredentialsMatcher(credentialsMatcher);
//给安全管理器设置realm
// securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
securityManager.setRealm(realm);
//SecurityUtils安全工具类
SecurityUtils.setSecurityManager(securityManager);
//拿到主体subject
Subject subject = SecurityUtils.getSubject();
//模拟登录,创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
//用户登录
try {
System.out.println("Authentication Status:"+subject.isAuthenticated());
subject.login(token);
System.out.println("Authentication Status:"+subject.isAuthenticated());
}
catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("认证失败,用户名不存在!");
}
catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("认证失败,密码错误!");
}
}
}
|
Shiro中的授权操作
关键对象
Shiro中的授权实现
编程式
1
2
3
4
5
6
7
|
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")){
//有权限
}
else{
//无权限
}
|
注解式
1
2
3
4
|
@RequireRoles("admin")
public void hello(){
//有权限
}
|
JSP标签式
1
2
3
4
|
<shiro:hasRole name="admin">
<!--有权限-->
</shiro:hasRole>
<!--注意:Thymeleaf中使用shiro需要额外集成-->
|
代码实现
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
78
79
80
81
82
83
84
85
86
87
|
public class CustomRealm extends AuthorizingRealm {
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("==================Authorization===================");
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
System.out.println(primaryPrincipal);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");
simpleAuthorizationInfo.addStringPermission("admin:create:*");
simpleAuthorizationInfo.addStringPermission("user:update:01");
return simpleAuthorizationInfo;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("==================Authentication===================");
String principal = (String) authenticationToken.getPrincipal();
String credentials = new String((char[]) authenticationToken.getCredentials());
System.out.println("************************************");
System.out.println(principal);
System.out.println(credentials);
if("zhangsan".equals(principal)){
return new SimpleAuthenticationInfo(principal,"44610a20602341c591e0c9b5ecf19ff3", ByteSource.Util.bytes("abc*"),this.getName());
}
return null;
}
}
public class TestAuthenticator
{
public static void main( String[] args )
{
//创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//新建自定义realm对象
CustomRealm realm = new CustomRealm();
//设置realm使用hash凭证检测器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(1024);//设置加盐散列次数
realm.setCredentialsMatcher(credentialsMatcher);
//给安全管理器设置realm
// securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
securityManager.setRealm(realm);
//SecurityUtils安全工具类
SecurityUtils.setSecurityManager(securityManager);
//拿到主体subject
Subject subject = SecurityUtils.getSubject();
//模拟登录,创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
//用户登录
try {
System.out.println("Authentication Status:"+subject.isAuthenticated());
subject.login(token);
System.out.println("Authentication Status:"+subject.isAuthenticated());
}
catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("认证失败,用户名不存在!");
}
catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("认证失败,密码错误!");
}
//进行授权操作
if(subject.isAuthenticated()){
System.out.println(subject.hasRole("user"));
System.out.println(subject.hasAllRoles(Arrays.asList("admin","user")));
System.out.println(subject.isPermitted("admin:create:*"));
System.out.println(subject.isPermitted("user:update:02"));
}
}
}
|
权限模型

Shiro和SSM的整合
整合思路
