一篇文章搞定Shiro权限管理框架
前言:前幾天學習了SpringSecurity安全框架,這幾天又接著學習shiro框架,這兩者框架都是同一類產品,解決同一類問題,但是在官方推薦使用Shiro框架,因為它簡單易學,所以這里有時間學習了以下。
Shiro的作用
用于驗證登陸用戶的身份
用戶訪問權限控制和登陸的認證,1.用于用戶登陸的驗證,2.用戶用戶登錄后的授權,也就是那些用戶擁有訪問那些接口的權限
可以響應認證、訪問控制,或者 Session 生命周期中發生的事件
可將一個或以上用戶安全數據源數據組合成一個復合的用戶“view”(視圖)
支持單點登錄(SSO)功能
支持提供“Remember Me”服務,當用戶第二次登陸時只要session還可用就不需要再次登陸
Shiro的優點
易于上手
靈活——Apache Shiro可以在任何應用程序環境中工作。雖然在網絡工作、EJB和IoC環境中可能并不需要它。但Shiro的授權也沒有任何規范,甚至沒有許多依賴關系。
Web支持——Apache Shiro擁有令人興奮的web應用程序支持,允許您基于應用程序的url創建靈活的安全策略和網絡協議(例如REST),同時還提供一組JSP庫控制頁面輸出。
低耦合——Shiro干凈的API和設計模式使它容易與許多其他框架和應用程序集成。你會看到Shiro無縫地集成Spring這樣的框架,以及Grails, Wicket, Tapestry, Mule, Apache Camel, Vaadin…等。
被廣泛支持——Apache Shiro是Apache軟件基金會的一部分。項目開發和用戶組都有友好的網民愿意幫助。這樣的商業公司如果需要Katasoft還提供專業的支持和服務。
Apache Shiro架構
下圖為描述Shiro的架構圖:
在這里插入圖片描述
Authentication(認證), Authorization(授權), Session Management(會話管理), Cryptography(加密)是Shiro框架的四大基石
Authentication(認證):用于用戶登陸時的認證
Authorization(授權):訪問控制。指定哪些用戶擁有訪問哪些接口的權限
Session Management(會話管理):特定于用戶的會話管理。
Cryptography(加密):對用戶的登陸的信息進行加密
其它特點:
Web支持:Shiro的Web支持API有助于保護Web應用程序。 緩存:緩存是Apache Shiro
API中的第一級,以確保安全操作保持快速和高效。
并發性:Apache Shiro支持具有并發功能的多線程應用程序。
測試:存在測試支持,可幫助您編寫單元測試和集成測試,并確保代碼按預期得到保障。
“運行方式”:允許用戶承擔另一個用戶的身份(如果允許)的功能,有時在管理方案中很有用。
“記住我”:記住用戶在會話中的身份,所以用戶只需要強制登錄即可。
注意: Shiro不會去維護用戶、維護權限,這些需要我們自己去設計/提供,然后通過相應的接口注入給Shiro
在概念層,Shiro 中的重要概念:Subject,SecurityManager和 Realm。
在這里插入圖片描述
Subject:當前用戶,Subject可以是一個人,但也可以是第三方服務、守護進程帳戶、時鐘守護任務或者其它–當前和軟件交互的任何事件。
SecurityManager:管理所有Subject,SecurityManager 是 Shiro架構的核心,配合內部安全組件共同組成安全傘。
Realms:用于進行權限信息的驗證,我們自己實現。Realm 本質上是一個特定的安全 DAO:它封裝與數據源連接的細節,得到Shiro所需的相關的數據。在配置 Shiro 的時候,你必須指定至少一個Realm來實現認證(authentication)和/或授權(authorization)。
Shiro 認證流程
在這里插入圖片描述
Shiro 實戰
(1)創建SpringBooot項目,pom.xml依賴如下:
4.0.0
org.springframework.boot
spring-boot-starter-parent
1.5.16.RELEASE
com.ldc.org
shirodemo
0.0.1-SNAPSHOT
war
shirodemo
Demo project for 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
88
89
90
91
SpringBoot整合Mybaties進行數據庫連接
SpringBoot整合Shiro框架
SpringBoot整合jsp并使用jstl表達式
SpringBoot整合阿里巴巴的Druid
(2)application.properties的配置如下:
database##
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=user
mybaties
mybatis.mapper-locations=mappers/*.xml
mybatis.type-aliases-package=com.ldc.org.shirodemo.pojo
jsp
spring.mvc.view.prefix=/pages/
spring.mvc.view.suffix=.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
這些都是一些常用的配置,這里就不再贅述,不懂得自行百度
(3)創建與數據庫對應的實體類,主要有User、Role、Permission這三個實體類。代碼如下:
package com.ldc.org.shirodemo.pojo;
public class Permission {
//get和set方法
}
package com.ldc.org.shirodemo.pojo;
import java.util.HashSet;
import java.util.Set;
public class Role {
}
package com.ldc.org.shirodemo.pojo;
import java.util.HashSet;
import java.util.Set;
public class User {
}
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
在這里插入圖片描述
(4)創建mapper接口
package com.ldc.org.shirodemo.mapper;
import com.ldc.org.shirodemo.pojo.User;
import org.apache.ibatis.annotations.Param;
public interface UserMapper {
User findByUsername(@Param("username") String username);}
1
2
3
4
5
6
7
8
9
在這里插入圖片描述
并在主方法的加上這些注解,指定mapper的位置,和開啟組件掃描
在這里插入圖片描述
(5)創建mapper.xml文件,代碼如下:
– 權限表–
CREATE TABLE permission (
pid int(11) NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL DEFAULT ‘’,
url VARCHAR(255) DEFAULT ‘’,
PRIMARY KEY (pid)
) ENGINE =InnoDB DEFAULT CHARSET = utf8;
INSERT INTO permission VALUES (‘1’,‘add’,’’);
INSERT INTO permission VALUES (‘2’,‘delete’,’’);
INSERT INTO permission VALUES (‘3’,‘edit’,’’);
INSERT INTO permission VALUES (‘4’,‘query’,’’);
– 用戶表–
CREATE TABLE user(
uid int(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(255) NOT NULL DEFAULT ‘’,
password VARCHAR(255) NOT NULL DEFAULT ‘’,
PRIMARY KEY (uid)
) ENGINE =InnoDB DEFAULT CHARSET=utf8;
INSERT INTO user VALUES (‘1’,‘admin’,‘123’);
INSERT INTO user VALUES (‘2’,‘demo’,‘123’);
– 角色表–
CREATE TABLE role(
rid int(11) NOT NULL AUTO_INCREMENT,
rname VARCHAR(255) NOT NULL DEFAULT ‘’,
PRIMARY KEY (rid)
) ENGINE =InnoDB DEFAULT CHARSET=utf8;
INSERT INTO role VALUES (‘1’,‘admin’);
INSERT INTO role VALUES (‘2’,‘customer’);
– 權限角色關系表–
CREATE TABLE permission_role(
rid int(11) NOT NULL ,
pid int(11) NOT NULL ,
KEY idx_rid(rid),
KEY idx_pid(pid)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;
INSERT INTO permission_role VALUES (‘1’,‘1’);
INSERT INTO permission_role VALUES (‘1’,‘2’);
INSERT INTO permission_role VALUES (‘1’,‘3’);
INSERT INTO permission_role VALUES (‘1’,‘4’);
INSERT INTO permission_role VALUES (‘2’,‘1’);
INSERT INTO permission_role VALUES (‘2’,‘4’);
– 用戶角色關系表–
CREATE TABLE user_role(
uid int(11) NOT NULL ,
rid int(11) NOT NULL ,
KEY idx_uid(uid),
KEY idx_rid(rid)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;
INSERT INTO user_role VALUES (1,1);
INSERT INTO user_role VALUES (2,2);
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
(7)創建AuthRealm,實現Shiro框架的AuthorizingRealm,代碼如下:
package com.ldc.org.shirodemo;
import com.ldc.org.shirodemo.pojo.Permission;
import com.ldc.org.shirodemo.pojo.Role;
import com.ldc.org.shirodemo.pojo.User;
import com.ldc.org.shirodemo.service.UserService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class AuthRealm extends AuthorizingRealm {
@Autowired private UserService userService;// 授權 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {User user= (User) principals.fromRealm(this.getClass().getName()).iterator().next();List<String> permissionList =new ArrayList<>();List<String> roleNameList =new ArrayList<>();Set<Role> roleSet=user.getRoles();if (CollectionUtils.isNotEmpty(roleSet)){for(Role role:roleSet){roleNameList.add(role.getRname());Set<Permission> permissionSet = role.getPermissions();if (CollectionUtils.isNotEmpty(permissionSet)){for(Permission permission:permissionSet){permissionList.add(permission.getName());}}}}SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();info.addStringPermissions(permissionList);info.addRoles(roleNameList);return info; }//認證登陸 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {UsernamePasswordToken usernamePasswordToken=(UsernamePasswordToken) token;String username = usernamePasswordToken.getUsername();User user = userService.findByUsername(username);return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());}}
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
(8)創建Shiro的配置類,如下:
package com.ldc.org.shirodemo;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.servlet.ShiroFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
@Configuration
public class ShiroConfiguration {
// filterChainDefinitionMap.put("/druid/",“anon”);
filterChainDefinitionMap.put("/",“user”);
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
}
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
(9)創建CredentialMatcher,作為密碼比較的規則驗證:
package com.ldc.org.shirodemo;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
/**
-
密碼校驗規則
*/
public class CredentialMatcher extends SimpleCredentialsMatcher {@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken usernamePasswordToken=(UsernamePasswordToken) token;
String password=new String(usernamePasswordToken.getPassword());
String dbPassword = (String) info.getCredentials();
return this.equals(password,dbPassword);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(10)Service層以及Service的實現:
package com.ldc.org.shirodemo.service;
import com.ldc.org.shirodemo.pojo.User;
public interface UserService {
User findByUsername( String username);}
1
2
3
4
5
6
7
8
9
10
package com.ldc.org.shirodemo.service;
import com.ldc.org.shirodemo.mapper.UserMapper;
import com.ldc.org.shirodemo.pojo.User;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserServiceImpl implements UserService {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(10)Controller層:
package com.ldc.org.shirodemo.controller;
import com.ldc.org.shirodemo.pojo.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
@Controller
public class UserController {
}
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
(11)jsp頁面:
<%@ page contentType=“text/html;charset=UTF-8” language=“java” %>
Home歡迎登陸, ${user.username}
1
2
3
4
5
6
7
8
9
10
11
12
<%@ page contentType=“text/html;charset=UTF-8” language=“java” %>
歡迎登陸
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page contentType=“text/html;charset=UTF-8” language=“java” %>
Unauthorized!
1
2
3
4
5
6
7
8
9
10
11
最后的項目的結構,如圖所示:
在這里插入圖片描述
(12)測試–其實前面粘貼了一大堆的代碼都是為后面的測試準備,這里講解的Shiro的技術點,主要有三點:
第一個是只有登錄后才能訪問相關的接口,沒有登陸是不允許訪問相關的接口,例如admin接口
第二個是某些接口只能被某些角色來訪問
第三點某些接口只能被特定權限的才能訪問
以下是驗證這三點的過程:
例如,index接口,只有登陸才能訪問,啟動項目,直接輸入localhost:8080/index是不能訪問首頁的,會被重定向會登陸頁面:
總結
以上是生活随笔為你收集整理的一篇文章搞定Shiro权限管理框架的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 工作奇谈——使用对称密匙加密数据
- 下一篇: 关于中宣部实名认证过程中的一些问题和解答