0%

Shiro 安全框架学习笔记

记录我学习安全框架Shiro的一些笔记。

Shiro VS Spring Security

Apache Shiro
简单灵活,可脱离Spring,粒度较粗;可以自己拓展,适合通过资源进行权限控制;
Spring Security
复杂笨重,不可脱离Spring,力度更细;适合做数据权限控制;

Shiro的认证过程

  1. 创建Security Manager
  2. 主体提交认证
  3. Security Manager认证
  4. Authenticator认证
  5. Realm认证

代码如下:
java代码

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
package com.leezy.shiro.shirotest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

public class AuthenticationTest {

SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

@Before
public void addUser() {
simpleAccountRealm.addAccount("LEEZY", "123456");
}

@Test
public void testAuthentication() {
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
// 将SimpleAccountRealm设置到环境变量中来
defaultSecurityManager.setRealm(simpleAccountRealm);

// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken token = new UsernamePasswordToken("LEEZY", "123456");
subject.login(token);

System.out.println("isAuthenticated: " + subject.isAuthenticated());
subject.isAuthenticated();

subject.logout();
System.out.println("isAuthenticated: " + subject.isAuthenticated());

}
}

pom文件

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
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.leezy.shiro</groupId>
<artifactId>shirotest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>shirotest</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>/.
</project>

Shiro授权

  1. 创建 Security Manager
  2. 主体授权
  3. Security Manager 授权
  4. Authorizer认证
  5. 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
package com.leezy.shiro.shirotest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

public class AuthenticationTest {

SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

@Before
public void addUser() {
simpleAccountRealm.addAccount("LEEZY", "123456", "admin", "user");
}

@Test
public void testAuthentication() {
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
// 将SimpleAccountRealm设置到环境变量中来
defaultSecurityManager.setRealm(simpleAccountRealm);

// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken token = new UsernamePasswordToken("LEEZY", "123456");
subject.login(token);

System.out.println("isAuthenticated: " + subject.isAuthenticated());
subject.isAuthenticated();

// 检查一个角色
// subject.checkRole("admin");
// 检查是否具备参数里所有的角色,通过遍历参数来进行授权
subject.checkRoles("admin", "user");
}
}

InitRealm的使用

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
package com.leezy.shiro.shirotest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

public class InitRealmTest {

@Test
public void testAuthentication() {

IniRealm iniRealm = new IniRealm("classpath:user.ini");

// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(iniRealm);

// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken token = new UsernamePasswordToken("LEEZY", "123456");
subject.login(token);

System.out.println("isAuthenticated: " + subject.isAuthenticated());

subject.checkRole("admin");

subject.checkPermission("user:delete");
subject.checkPermission("user:update");
}
}

user.ini

1
2
3
4
[users]
LEEZY=123456,admin
[roles]
admin=user:delete,user:update

JDBCRealm的使用

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
package com.leezy.shiro.shirotest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

import com.alibaba.druid.pool.DruidDataSource;

public class JdbcRealmTest {

DruidDataSource dataSource = new DruidDataSource();

{
dataSource.setUrl("jdbc:mysql://192.168.56.101:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("Zdh!123456");
}

@Test
public void testAuthentication() {

JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
// 开启后才可以进行权限认证(默认关闭)
jdbcRealm.setPermissionsLookupEnabled(true);

String select_sql = "select password from test_user where username = ?";
jdbcRealm.setAuthenticationQuery(select_sql);

String role_sql = "select role_name from test_user_role where user_name = ?";
jdbcRealm.setUserRolesQuery(role_sql);

// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);

// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken token = new UsernamePasswordToken("SAKURA", "123456");
subject.login(token);

System.out.println("isAuthenticated: " + subject.isAuthenticated());

subject.checkRole("admin");

subject.checkPermission("user:select");
}
}

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
<!-- MySQL驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.14</version>
</dependency>

查看JdbcRealm源码可以发现,它有默认的SQL查询语句,只要建立相应的数据表就可以。

自定义Realm

自定义Realm继承AuthorizingRealm

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
package com.leezy.shiro.realm;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class CustomRealm extends AuthorizingRealm{
Map<String, String> userMap = new HashMap<String, String>(16);

{
userMap.put("LEEZY", "123456");
super.setName("CustomRealm");
}

// 授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
// 从数据库或者缓存中获取角色数据
Set<String> roles = getRolesByUserName(username);

Set<String> permissions = getPermissionsByUserName(username);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(roles);
info.setStringPermissions(permissions);
return info;
}

// 认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

//1 从主体传过来的认证信息中获得用户名
String username = (String) token.getPrincipal();

//2.通过用户名到数据库中获取凭证
String password = getPasswordByUserName(username);

if(password == null) {
return null;
}

SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("LEEZY", password, "CustomRealm");

return authenticationInfo;
}

// 模拟数据库中权限查询
private Set<String> getPermissionsByUserName(String username) {
Set<String> sets = new HashSet<String>();
sets.add("user:delete");
sets.add("user:add");
return sets;
}

// 模拟数据库中角色查询
public Set<String> getRolesByUserName(String username) {
Set<String> sets = new HashSet<String>();
sets.add("admin");
sets.add("user");
return sets;
}

// 模拟数据库的查询凭证
private String getPasswordByUserName(String username) {
return userMap.get(username);
}

}
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
package com.leezy.shiro.shirotest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

import com.leezy.shiro.realm.CustomRealm;

public class CustomRealmTest {

@Test
public void testAuthentication() {

CustomRealm customRealm = new CustomRealm();
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(customRealm);

// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken token = new UsernamePasswordToken("LEEZY", "123456");
subject.login(token);

System.out.println("isAuthenticated: " + subject.isAuthenticated());

subject.checkRole("admin");

subject.checkPermission("user:delete");
subject.checkPermission("user:add");
}
}

Shiro加密

Shiro加密方式:

  1. HashedCredentialsMatcher
  2. 自定义Realm中使用散列
  3. 盐的使用
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
package com.leezy.shiro.realm;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

public class CustomRealm extends AuthorizingRealm{
Map<String, String> userMap = new HashMap<String, String>(16);

{
// 使用加密之后的密文
userMap.put("LEEZY", "e43a7da8514a1c9d566164b3ea731a4a");
super.setName("CustomRealm");
}

// 授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
// 从数据库或者缓存中获取角色数据
Set<String> roles = getRolesByUserName(username);

Set<String> permissions = getPermissionsByUserName(username);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(roles);
info.setStringPermissions(permissions);
return info;
}

// 认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

//1 从主体传过来的认证信息中获得用户名
String username = (String) token.getPrincipal();

//2.通过用户名到数据库中获取凭证
String password = getPasswordByUserName(username);

if(password == null) {
return null;
}

SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("LEEZY", password, "CustomRealm");
// 将盐设置进去
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("SAKURA"));
return authenticationInfo;
}

// 模拟数据库中权限查询
private Set<String> getPermissionsByUserName(String username) {
Set<String> sets = new HashSet<String>();
sets.add("user:delete");
sets.add("user:add");
return sets;
}

// 模拟数据库中角色查询
public Set<String> getRolesByUserName(String username) {
Set<String> sets = new HashSet<String>();
sets.add("admin");
sets.add("user");
return sets;
}

// 模拟数据库的查询凭证
private String getPasswordByUserName(String username) {
return userMap.get(username);
}

public static void main(String[] args) {
// MD5 + 加盐
Md5Hash md5Hash = new Md5Hash("123456", "SAKURA");
System.out.println(md5Hash.toString());
}
}

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
package com.leezy.shiro.shirotest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

import com.leezy.shiro.realm.CustomRealm;

public class CustomRealmTest {

@Test
public void testAuthentication() {

CustomRealm customRealm = new CustomRealm();
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(customRealm);

HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
// 设置加密方式
matcher.setHashAlgorithmName("md5");
// 设置加密次数
matcher.setHashIterations(1);
customRealm.setCredentialsMatcher(matcher);

// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken token = new UsernamePasswordToken("LEEZY", "123456");
subject.login(token);

System.out.println("isAuthenticated: " + subject.isAuthenticated());

subject.checkRole("admin");

subject.checkPermission("user:delete");
subject.checkPermission("user:add");
}
}

Shiro集成Spring

// TODO