什么是ACL和RBAC
ACL
- Access Control list:訪問控制串列
- 優點:簡單易用,開發便捷
- 缺點:用戶和權限直接掛鉤,導致在授予時的復雜性,比較分散,不便于管理
- 例子:常見的檔案系統權限設計,直接給用戶加權限
RBAC
- Role Based Access Control:基于角色的訪問控制
- 權限與角色相關聯,用戶通過成為適當角色的成員而得到這些角色的權限
- 優點:簡化了用戶與權限的管理,通過對用戶進行分類,使得角色與權限關聯起來
- 缺點:開發比ACL相對復雜
- 例子:基于RBAC模型的權限驗證框架,Apache Shiro
什么是Apache Shiro
官網地址
點我直達
介紹
Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理,使用Shiro的易于理解的API,您可以快速、輕松地獲得任何應用程式,從最小的移動應用程式到最大的網路和企業應用程式,
什么是身份認證
Authentication,身份認證,一般就是登陸校驗
什么是授權
Authorization,給用戶分配角色或者訪問某些資源的權限
什么是會話管理
Session Management,用戶的會話管理員,多數情況下是web session
什么是加密
Cryptography,資料加密,比如密碼加解密
核心概念
Subject
我們把用戶或者程式稱為主體,主體去訪問系統或者資源
SecurityManager
安全管理器,Subject的認證和授權都要在安全管理器下進行
Realm
資料域,Shiro和安全資料的連接器,通過realm獲取認證授權相關資訊
Authenticator
認證器,主要負責Subject的認證
Authorizer
授權器,主要負責Subject的授權,控制Subject擁有的角色或者權限
Crytography
加解密,Shiro的包含易于使用和理解的資料加解密方法,簡化了很多復雜的API
Cache Manager
快取管理器,比如認證或授權資訊,通過快取進行管理,提高性能
快速上手
構建專案

認證和授權

package com.ybchen.springboot_shiro; 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; /** * @Description: * @Author:chenyanbin * @Date:2020/12/27 7:43 下午 * @Versiion:1.0 */ public class QuickStartTest { private DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); private SimpleAccountRealm accountRealm = new SimpleAccountRealm(); @Before public void init() { //初始化資料源,模擬從資料庫中取的資料 accountRealm.addAccount("laochen", "123"); accountRealm.addAccount("laowang", "123456"); //構建環境 defaultSecurityManager.setRealm(accountRealm); } @Test public void testAuthentication() { //設定背景關系 SecurityUtils.setSecurityManager(defaultSecurityManager); //獲取當前主體 Subject subject = SecurityUtils.getSubject(); //模擬用戶登錄,賬戶、密碼 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("laowang", "123456"); subject.login(usernamePasswordToken); //判斷是否成功 boolean authenticated = subject.isAuthenticated(); System.out.println("認證結果:" + authenticated); } }QuickStartTest.java
常用API
//是否有對應的角色
subject.hasRole("root");
//獲取subject名
subject.getPrincipal();
//檢查是否有對應的角色,無回傳值,直接在SecurityManager里面進行判斷
subject.checkRole("admin");
//檢查是否有對應的角色
subject.hasRole("admin");
//退出登錄
subject.logout();

package com.ybchen.springboot_shiro;
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;
/**
* @Description:
* @Author:chenyanbin
* @Date:2020/12/27 7:43 下午
* @Versiion:1.0
*/
public class QuickStartAPITest {
private DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
private SimpleAccountRealm accountRealm = new SimpleAccountRealm();
@Before
public void init() {
//初始化資料源,模擬從資料庫中取的資料
accountRealm.addAccount("laochen", "123","root","admin");
accountRealm.addAccount("laowang", "123456","user");
//構建環境
defaultSecurityManager.setRealm(accountRealm);
}
@Test
public void testAuthentication() {
//設定背景關系
SecurityUtils.setSecurityManager(defaultSecurityManager);
//獲取當前主體
Subject subject = SecurityUtils.getSubject();
//模擬用戶登錄,賬戶、密碼
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("laochen", "123");
subject.login(usernamePasswordToken);
//判斷是否成功
boolean authenticated = subject.isAuthenticated();
System.out.println("認證結果:" + authenticated);
//是否有對應的角色
System.out.println("是否有對應的root角色:"+subject.hasRole("root"));
//獲取subject名
System.out.println("獲取subject名:"+subject.getPrincipal());
//檢查是否有對應的角色,無回傳值,直接在SecurityManager里面進行判斷,沒有的話,直接報錯
subject.checkRole("admin");
//檢查是否有對應的角色
System.out.println("是否存在admin角色:"+subject.hasRole("admin"));
//退出登錄
subject.logout();
System.out.println("退出登錄后,認證結果:" + authenticated);
}
}
QuickStartAPITest.java
<?xml version="1.0" encoding="UTF-8"?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.ybchen</groupId> <artifactId>springboot_shiro</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot_shiro</name> <description>SpringBoot整合Shiro</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.3</version> </dependency> <!--Shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>pom.xml
realm實戰
作用
Shiro從Realm獲取安全資料
概念
- principal:主體的標識,可以有多個,但是需要具有唯一性,如:手機號、郵箱
- credential:憑證,一般就是密碼
內置ini realm

package com.ybchen.springboot_shiro; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.junit.Before; import org.junit.Test; /** * @Description:從ini組態檔中讀取用戶與角色 * @Author:chenyanbin * @Date:2020/12/27 8:52 下午 * @Versiion:1.0 */ public class QuickStartIniTest { @Before public void init() { } @Test public void testAuthentication() { //創建SecurityManager工廠,通過組態檔ini創建 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager=factory.getInstance(); //將securityManager設定到當前運行環境中 SecurityUtils.setSecurityManager(securityManager); //獲取主體 Subject subject = SecurityUtils.getSubject(); //模擬用戶登錄,賬戶、密碼 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("laochen", "123"); subject.login(usernamePasswordToken); //判斷是否成功 //判斷是否成功 boolean authenticated = subject.isAuthenticated(); System.out.println("認證結果:" + authenticated); //是否有對應的角色 System.out.println("是否有對應的root角色:"+subject.hasRole("root")); //獲取subject名 System.out.println("獲取subject名:"+subject.getPrincipal()); //檢查是否有對應的角色,無回傳值,直接在SecurityManager里面進行判斷,沒有的話,直接報錯 subject.checkRole("admin"); //檢查是否有對應的角色 System.out.println("是否存在admin角色:"+subject.hasRole("admin")); //退出登錄 subject.logout(); System.out.println("退出登錄后,認證結果:" + authenticated); } }QuickStartIniTest.java
# 格式 name=password,role1,role2,..roleN
[users]
# 賬戶=laochen;密碼=123;角色=admin
laochen = 123, admin
laowang = 456, user
# 格式 role=permission1,permission2...permissionN 也可以用通配符
# 下面配置user的權限為所有video:find,video:buy,如果需要配置video全部操作crud 則 user = video:*
[roles]
user = video:find,video:buy
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
shiro.ini
校驗權限

package com.ybchen.springboot_shiro; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.junit.Before; import org.junit.Test; /** * @Description:從ini組態檔中讀取用戶與角色 * @Author:chenyanbin * @Date:2020/12/27 8:52 下午 * @Versiion:1.0 */ public class QuickStartIniTest { @Before public void init() { } @Test public void testAuthentication() { //創建SecurityManager工廠,通過組態檔ini創建 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager=factory.getInstance(); //將securityManager設定到當前運行環境中 SecurityUtils.setSecurityManager(securityManager); //獲取主體 Subject subject = SecurityUtils.getSubject(); //模擬用戶登錄,賬戶、密碼 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("laochen", "123"); subject.login(usernamePasswordToken); //判斷是否成功 //判斷是否成功 boolean authenticated = subject.isAuthenticated(); System.out.println("認證結果:" + authenticated); //是否有對應的角色 System.out.println("是否有對應的root角色:"+subject.hasRole("root")); //獲取subject名 System.out.println("獲取subject名:"+subject.getPrincipal()); //檢查是否有對應的角色,無回傳值,直接在SecurityManager里面進行判斷,沒有的話,直接報錯 subject.checkRole("admin"); //檢查是否有對應的角色 System.out.println("是否存在admin角色:"+subject.hasRole("admin")); //================權限,沒有的話直接報錯================ subject.checkPermission("video:delete"); System.out.println("是否有video:delete權限:"+subject.isPermitted("video:delete")); //退出登錄 subject.logout(); System.out.println("退出登錄后,認證結果:" + subject.isAuthenticated()); } }QuickStartIniTest.java
# 格式 name=password,role1,role2,..roleN
[users]
# 賬戶=laochen;密碼=123;角色=admin
laochen = 123, admin
laowang = 456, user
# 格式 role=permission1,permission2...permissionN 也可以用通配符
# 下面配置user的權限為所有video:find,video:buy,如果需要配置video全部操作crud 則 user = video:*
[roles]
user = video:find,video:buy
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
shiro.ini
注:組態檔必須ini結尾
內置JdbcRealm
方式一

package com.ybchen.springboot_shiro; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.junit.Test; /** * @Description: * @Author:chenyanbin * @Date:2020/12/27 10:40 下午 * @Versiion:1.0 */ public class QuickStartJdbcIniTest { @Test public void testAuthentication(){ //創建SecurityManager工廠,通過組態檔ini創建 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:jdbcrealm.ini"); SecurityManager securityManager=factory.getInstance(); //將securityManager設定到當前運行環境中 SecurityUtils.setSecurityManager(securityManager); //獲取主體 Subject subject = SecurityUtils.getSubject(); //模擬用戶登錄,賬戶、密碼 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("laochen", "123"); subject.login(usernamePasswordToken); //判斷是否成功 boolean authenticated = subject.isAuthenticated(); System.out.println("認證結果:" + authenticated); //是否有對應的角色 System.out.println("是否有對應的root角色:"+subject.hasRole("root")); //獲取subject名 System.out.println("獲取subject名:"+subject.getPrincipal()); //檢查是否有對應的角色,無回傳值,直接在SecurityManager里面進行判斷,沒有的話,直接報錯 subject.checkRole("role1"); //檢查是否有對應的角色 System.out.println("是否存在role1角色:"+subject.hasRole("role1")); //================權限,沒有的話直接報錯================ //subject.checkPermission("video:delete"); System.out.println("是否有video:buy權限:"+subject.isPermitted("video:buy")); //退出登錄 subject.logout(); System.out.println("退出登錄后,認證結果:" + subject.isAuthenticated()); } }QuickStartJdbcIniTest.java
#宣告Realm,指定realm型別 jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm #配置資料源 #dataSource=com.mchange.v2.c3p0.ComboPooledDataSource dataSource=com.alibaba.druid.pool.DruidDataSource # mysql-connector-java 5 用的驅動url是com.mysql.jdbc.Driver,mysql-connector-java6以后用的是com.mysql.cj.jdbc.Driver dataSource.driverClassName=com.mysql.cj.jdbc.Driver #避免安全警告 dataSource.url=jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false #賬號、密碼 dataSource.username=root dataSource.password=root #指定資料源 jdbcRealm.dataSource=$dataSource #開啟查找權限, 默認是false jdbcRealm.permissionsLookupEnabled=true #指定SecurityManager的Realms實作,設定realms,可以有多個,用逗號隔開 securityManager.realms=$jdbcRealmjdbcrealm.ini
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50731
Source Host : localhost:3306
Source Schema : shiro
Target Server Type : MySQL
Target Server Version : 50731
File Encoding : 65001
Date: 27/12/2020 23:06:45
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for roles_permissions
-- ----------------------------
DROP TABLE IF EXISTS `roles_permissions`;
CREATE TABLE `roles_permissions` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`role_name` varchar(100) DEFAULT NULL COMMENT '角色名',
`permission` varchar(100) DEFAULT NULL COMMENT '權限名',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_roles_permissions` (`role_name`,`permission`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of roles_permissions
-- ----------------------------
BEGIN;
INSERT INTO `roles_permissions` VALUES (4, 'admin', 'video:*');
INSERT INTO `roles_permissions` VALUES (3, 'role1', 'video:buy');
INSERT INTO `roles_permissions` VALUES (2, 'role1', 'video:find');
INSERT INTO `roles_permissions` VALUES (5, 'role2', '*');
INSERT INTO `roles_permissions` VALUES (1, 'root', '*');
COMMIT;
-- ----------------------------
-- Table structure for user_roles
-- ----------------------------
DROP TABLE IF EXISTS `user_roles`;
CREATE TABLE `user_roles` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`username` varchar(100) DEFAULT NULL COMMENT '用戶名',
`role_name` varchar(100) DEFAULT NULL COMMENT '角色名',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_roles` (`username`,`role_name`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_roles
-- ----------------------------
BEGIN;
INSERT INTO `user_roles` VALUES (1, 'laochen', 'role1');
INSERT INTO `user_roles` VALUES (2, 'laochen', 'role3');
INSERT INTO `user_roles` VALUES (4, 'laowang', 'admin');
INSERT INTO `user_roles` VALUES (3, 'laowang', 'root');
COMMIT;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`username` varchar(100) DEFAULT NULL COMMENT '用戶名',
`password` varchar(100) DEFAULT NULL COMMENT '密碼',
`password_salt` varchar(100) DEFAULT NULL COMMENT '密碼加鹽規則',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_users_username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of users
-- ----------------------------
BEGIN;
INSERT INTO `users` VALUES (1, 'laochen', '123', NULL);
INSERT INTO `users` VALUES (2, 'laowang', '456', NULL);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
建表陳述句.sql
注意
表名和欄位要對應上,否則自定義定,繼承:AuthorizingRealm,重寫sql查詢陳述句!!!!并重新指定realm型別!!!!

方式二

package com.ybchen.springboot_shiro; import com.alibaba.druid.pool.DruidDataSource; 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; /** * @Description: * @Author:chenyanbin * @Date:2020/12/27 10:40 下午 * @Versiion:1.0 */ public class QuickStartJdbc2Test { @Test public void testAuthentication() { DefaultSecurityManager securityManager = new DefaultSecurityManager(); DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.cj.jdbc.Driver"); ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false"); ds.setUsername("root"); ds.setPassword("root"); JdbcRealm jdbcRealm = new JdbcRealm(); //開啟查找權限, 默認是false jdbcRealm.setPermissionsLookupEnabled(true); //配置資料源 jdbcRealm.setDataSource(ds); //jdbc與DefaultSecurityManager關聯 securityManager.setRealm(jdbcRealm); //=======================下面內容相同============================== //將securityManager設定到當前運行環境中 SecurityUtils.setSecurityManager(securityManager); //獲取主體 Subject subject = SecurityUtils.getSubject(); //模擬用戶登錄,賬戶、密碼 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("laochen", "123"); subject.login(usernamePasswordToken); //判斷是否成功 boolean authenticated = subject.isAuthenticated(); System.out.println("認證結果:" + authenticated); //是否有對應的角色 System.out.println("是否有對應的root角色:" + subject.hasRole("root")); //獲取subject名 System.out.println("獲取subject名:" + subject.getPrincipal()); //檢查是否有對應的角色,無回傳值,直接在SecurityManager里面進行判斷,沒有的話,直接報錯 subject.checkRole("role1"); //檢查是否有對應的角色 System.out.println("是否存在role1角色:" + subject.hasRole("role1")); //================權限,沒有的話直接報錯================ //subject.checkPermission("video:delete"); System.out.println("是否有video:buy權限:" + subject.isPermitted("video:buy")); //退出登錄 subject.logout(); System.out.println("退出登錄后,認證結果:" + subject.isAuthenticated()); } }QuickStartJdbc2Test.java
自定義realm
繼承AuthorizingRealm,重寫授權方法doGetAuthorizationInfo、重寫認證方法doGetAuthenticationInfo,
UsernamePasswordToken:對應就是shiro的token中有Principal和Credential,
SimpleAuthorizationInfo:代表用戶角色權限資訊
SimpleAuthenticationInfo:代表該用戶的認證資訊

package com.ybchen.springboot_shiro; 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; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * @Description:自定義Realm * @Author:chenyanbin * @Date:2020/12/28 8:41 下午 * @Versiion:1.0 */ public class CustomRealm extends AuthorizingRealm { private final Map<String, String> userInfoMap = new HashMap<>(); //role-->permission private final Map<String, Set<String>> permissionMap = new HashMap<>(); //user-->role private final Map<String, Set<String>> roleMap = new HashMap<>(); /** * 代碼塊初始化資料 */ { userInfoMap.put("laochen", "123"); userInfoMap.put("laowang", "456"); //================================ Set<String> set1 = new HashSet<>(); set1.add("video:find"); set1.add("video:buy"); Set<String> set2 = new HashSet<>(); set2.add("video:add"); set2.add("video:delete"); permissionMap.put("laochen", set1); permissionMap.put("laowang", set2); //================================ Set<String> set3 = new HashSet<>(); Set<String> set4 = new HashSet<>(); set3.add("role1"); set3.add("role2"); set4.add("root"); roleMap.put("laochen", set3); roleMap.put("laowang", set4); } /** * 授權認證 * * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("授權 AuthorizationInfo"); String name = (String) principals.getPrimaryPrincipal(); //權限 Set<String> permissions = getPermissionsByNameFromDB(name); //角色 Set<String> roles = getRoleByNameFromDB(name); SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo(); simpleAuthorizationInfo.setRoles(roles); simpleAuthorizationInfo.setStringPermissions(permissions); return simpleAuthorizationInfo; } /** * 模擬從資料庫中取角色 * * @param name * @return */ private Set<String> getRoleByNameFromDB(String name) { return roleMap.get(name); } /** * 模擬從資料庫中取權限 * * @param name * @return */ private Set<String> getPermissionsByNameFromDB(String name) { return permissionMap.get(name); } /** * 登錄認證 * * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("認證 doGetAuthenticationInfo"); //用戶名 String name = (String) token.getPrincipal(); //從DB中根據用戶取密碼 String pwd = getPwdByUserNameFromDB(name); if (pwd == null || "".equals(pwd)) { return null; } SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, pwd, this.getName()); return simpleAuthenticationInfo; } /** * 模擬從資料庫中取密碼 * * @param name * @return */ private String getPwdByUserNameFromDB(String name) { return userInfoMap.get(name); } }CustomRealm.java
package com.ybchen.springboot_shiro; 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.Before; import org.junit.Test; /** * @Description:自定義realm * @Author:chenyanbin * @Date:2020/12/28 8:43 下午 * @Versiion:1.0 */ public class QuickCustomRealmTest { private CustomRealm customRealm=new CustomRealm(); private DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); @Before public void init() { //構建環境 defaultSecurityManager.setRealm(customRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); } @Test public void testAuthentication(){ //獲取當前操作的主體 Subject subject = SecurityUtils.getSubject(); //用戶輸入賬號、密碼 UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken("laochen","123"); subject.login(usernamePasswordToken); System.out.println("認證結果:"+subject.isAuthenticated()); //拿到主體標識屬性 System.out.println("獲取subject名:"+subject.getPrincipal()); //是否有role1角色,沒有則報錯 subject.checkRole("role1"); //是否有對應的角色 System.out.println("是否有對應的角色:"+subject.hasRole("role1")); //是否有對應的權限 System.out.println("是否有對應的權限:"+subject.isPermitted("video:find")); } }QuickCustomRealmTest.java
Filter過濾器
- 核心過濾器
- DefaultFilter,配置那個路徑對應那個攔截器進行處理
- authc:org.apache.shiro.web.filter.authc.FormAuthenticationFilter
- 需要認證登錄才能訪問
- user:org.apache.shiro.web.filter.authc.UseerrFilter
- 用戶攔截器,表示必須存在用戶
- anon:org.apache.shiro.web.filter.authc.AnonymoousFilter
- 匿名攔截器,不需要登錄即可訪問的資源,匿名用戶或游客,一般用于過濾靜態資源,
- roles:org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
- 角色授權攔截器,驗證用戶是否擁有角色
- 引數可寫多個,表示某些角色才能通過,多個引數時,寫roles["root,role1"],當有多個引數時必須每個引數都通過才算通過
- perms:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
- 權限授權攔截器,驗證用戶是否擁有權限
- 引數可寫多個,表示需要某些權限才能通過,多個引數寫perms["user,admin"],當有多個引數時必須每個引數都通過才算可以
- authcBasci:org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
- httpBasic,身份驗證攔截器
- logout:org.apache.shiro.web.filter.authc.LogoutFilter
- 退出攔截器,執行后會直接跳轉到shiroFilterFactoryBean.setLoginUrl(),設定的url
- port:org.apache.shiro.web.filter.authz.PortFilter
- 埠攔截器,可通過的埠
- ssl:org.apache.shiro.web.filter.authz.SslFilter
- ssl攔截器,只有請求協議是https才能通過

Filter配置路徑
- 路徑通配符支持?、*、**,注意通配符匹配不包含目錄分隔符“/”
- *:可以匹配所有,不加*,可以進行前綴匹配,但多個冒號就需要多個*來匹配
url權限采取第一次匹配優先的方式 ?:匹配一個字符,如:/user?,匹配:/user1,但不匹配:/user/ *:匹配零個或多個字串,如:/add*,匹配:/addtest,但不匹配:/user/1 **:匹配路徑中的零個或多個路徑,如:/user/**將匹配:/user/xxx/yyy
Shiro權限控制注解
注解方式
- @RequiresRoles(value=https://www.cnblogs.com/chenyanbin/p/{"admin","editor"},logical=Logical.AND)
- 需要角色:admin和editor兩個角色,AND表示兩個同時成立
- RequiresPermissions(value=https://www.cnblogs.com/chenyanbin/p/{"user:add","user:del"},logical.OR)
- 需要權限user:add或user:del權限其中一個,OR是或的意思
- @RequiresAuthentication
- 已經授過權,呼叫Subject.isAuthenticated()回傳true
- @RequiresUser
- 身份驗證或通過記住我登錄過的

使用檔案的方式
使用ShiroConfig,
編程方式

SpringBoot整合Shiro
技術堆疊
前后端分離+SpringBoot+Mysql+Mybatis+Shiro+Redis+JDK8
資料庫表
/* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 50731 Source Host : localhost:3306 Source Schema : shiro_2 Target Server Type : MySQL Target Server Version : 50731 File Encoding : 65001 Date: 03/01/2021 22:36:28 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for permission -- ---------------------------- DROP TABLE IF EXISTS `permission`; CREATE TABLE `permission` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `name` varchar(255) DEFAULT NULL COMMENT '權限名稱', `url` varchar(255) DEFAULT NULL COMMENT '路徑', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='權限'; -- ---------------------------- -- Records of permission -- ---------------------------- BEGIN; INSERT INTO `permission` VALUES (1, 'video_update', '/api/video/update'); INSERT INTO `permission` VALUES (2, 'video_delete', '/api/video/delete'); INSERT INTO `permission` VALUES (3, 'video_add', '/api/video/add'); INSERT INTO `permission` VALUES (4, 'order_list', '/api/order/list'); INSERT INTO `permission` VALUES (5, 'user_list', '/api/user/list'); COMMIT; -- ---------------------------- -- Table structure for role -- ---------------------------- DROP TABLE IF EXISTS `role`; CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `name` varchar(255) DEFAULT NULL COMMENT '角色名稱', `description` varchar(255) DEFAULT NULL COMMENT '描述', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='角色'; -- ---------------------------- -- Records of role -- ---------------------------- BEGIN; INSERT INTO `role` VALUES (1, 'admin', '系統管理員'); INSERT INTO `role` VALUES (2, 'root', '超級管理員'); INSERT INTO `role` VALUES (3, 'user', '普通用戶'); COMMIT; -- ---------------------------- -- Table structure for role_permission -- ---------------------------- DROP TABLE IF EXISTS `role_permission`; CREATE TABLE `role_permission` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `role_id` int(11) DEFAULT NULL COMMENT '角色id', `permission_id` int(11) DEFAULT NULL COMMENT '權限id', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='角色-權限'; -- ---------------------------- -- Records of role_permission -- ---------------------------- BEGIN; INSERT INTO `role_permission` VALUES (1, 1, 1); INSERT INTO `role_permission` VALUES (2, 1, 2); INSERT INTO `role_permission` VALUES (3, 2, 1); INSERT INTO `role_permission` VALUES (4, 2, 2); INSERT INTO `role_permission` VALUES (5, 2, 3); INSERT INTO `role_permission` VALUES (6, 2, 4); INSERT INTO `role_permission` VALUES (7, 2, 5); INSERT INTO `role_permission` VALUES (8, 3, 5); COMMIT; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `username` varchar(255) DEFAULT NULL COMMENT '用戶名', `password` varchar(255) DEFAULT NULL COMMENT '密碼', `create_time` datetime DEFAULT NULL COMMENT '創建時間', `salt` varchar(255) DEFAULT NULL COMMENT '加鹽', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='用戶'; -- ---------------------------- -- Records of user -- ---------------------------- BEGIN; INSERT INTO `user` VALUES (1, 'laochen', '123', NULL, NULL); INSERT INTO `user` VALUES (2, 'laowang', '456', NULL, NULL); INSERT INTO `user` VALUES (3, 'laoli', '789', NULL, NULL); COMMIT; -- ---------------------------- -- Table structure for user_role -- ---------------------------- DROP TABLE IF EXISTS `user_role`; CREATE TABLE `user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `role_id` int(11) DEFAULT NULL COMMENT '角色id', `user_id` int(11) DEFAULT NULL COMMENT '用戶id', `remark` varchar(255) DEFAULT NULL COMMENT '備注', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='角色-用戶關聯表'; -- ---------------------------- -- Records of user_role -- ---------------------------- BEGIN; INSERT INTO `user_role` VALUES (1, 1, 1, 'laochen是系統管理員'); INSERT INTO `user_role` VALUES (2, 2, 2, 'laowang是超級管理員'); INSERT INTO `user_role` VALUES (3, 3, 3, 'laoli是普通用戶'); INSERT INTO `user_role` VALUES (4, 1, 2, 'laowang是系統管理員'); COMMIT; SET FOREIGN_KEY_CHECKS = 1;shiro_2.sql
專案結構

package com.ybchen.springboot_shiro.config; import com.ybchen.springboot_shiro.domain.Role; import com.ybchen.springboot_shiro.domain.User; import com.ybchen.springboot_shiro.service.UserService; 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; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @Description:自定義realm * @Author:chenyanbin * @Date:2021/1/2 11:16 下午 * @Versiion:1.0 */ public class CustomRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 進行權限校驗的時候會呼叫 * * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("CustomRealm doGetAuthorizationInfo 授權"); //獲取用戶名 String userName = (String) principals.getPrimaryPrincipal(); User user = userService.findAllUserInfoByUserName(userName); if (user == null) { return null; } //角色集合 List<String> stringRoleList = new ArrayList<>(); //權限集合 List<String> stringPermissionList = new ArrayList<>(); List<Role> roleList = user.getRoleList(); stringRoleList = roleList.stream().map( obj -> { stringPermissionList.addAll(obj.getPermissionList() .stream() .map(per -> per.getName()).collect(Collectors.toList())); return obj.getName(); }).collect(Collectors.toList()); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.addRoles(stringRoleList); simpleAuthorizationInfo.addStringPermissions(stringPermissionList); return simpleAuthorizationInfo; } /** * 用戶登錄的時候會呼叫 * * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("CustomRealm doGetAuthenticationInfo 認證"); //從token中獲取用戶資訊 String uesrName = (String) token.getPrincipal(); User user = userService.findAllUserInfoByUserName(uesrName); if (user == null) { return null; } //密碼 String pwd = user.getPassword(); if (pwd == null || "".equals(pwd)) { return null; } return new SimpleAuthenticationInfo(uesrName, pwd, this.getClass().getName()); } }CustomRealm.java
package com.ybchen.springboot_shiro.config; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.util.WebUtils; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.Serializable; /** * @Description:自定義SessionManager * @Author:chenyanbin * @Date:2021/1/3 4:54 下午 * @Versiion:1.0 */ public class CustomSessionManager extends DefaultWebSessionManager { public static final String AUTHORIZATION="token"; public CustomSessionManager() { super(); } @Override protected Serializable getSessionId(ServletRequest request, ServletResponse response) { //獲取sessionId String sessionId= WebUtils.toHttp(request).getHeader(AUTHORIZATION); if (sessionId!=null){ request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return sessionId; }else { return super.getSessionId(request,response); } } }CustomSessionManager.java
package com.ybchen.springboot_shiro.config; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 4:12 下午 * @Versiion:1.0 */ @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { System.out.println("ShiroConfig ShiroFilterFactoryBean 執行"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //設定SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //如果訪問需要登錄的某個介面,卻沒有登錄,則呼叫此介面(如果不是前后端分離,則跳轉頁面) shiroFilterFactoryBean.setLoginUrl("/pub/need_login"); //shiroFilterFactoryBean.setLoginUrl("/xxx.jsp"); //登錄成功后,跳轉的鏈接,若前后端分離,沒必要設定這個 //shiroFilterFactoryBean.setSuccessUrl(""); //登錄成功,未授權會呼叫此方法 shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_permit"); //攔截路徑,必須使用:LinkedHashMap,要不然攔截效果會時有時無,因為使用的是無序的Map Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //key=正則運算式路徑,value=https://www.cnblogs.com/chenyanbin/p/org.apache.shiro.web.filter.mgt.DefaultFilter //退出過濾器 filterChainDefinitionMap.put("/logout", "logout"); //匿名可以訪問,游客模式 filterChainDefinitionMap.put("/pub/**", "anon"); //登錄用戶才可以訪問 filterChainDefinitionMap.put("/authc/**", "authc"); //管理員角色才能訪問 filterChainDefinitionMap.put("/admin/**", "roles[admin]"); //有編輯權限才能訪問 filterChainDefinitionMap.put("/video/update", "perms[video_update]"); //authc:url必須通過認證才可以訪問 //anon:url可以匿名訪問 //過濾鏈是順序執行,從上而下,一般把/**,放到最下面 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //如果不是前后端分離,不用設定setSessionManager securityManager.setSessionManager(sessionManager()); securityManager.setRealm(customRealm()); return securityManager; } /** * 自定義realm * * @return */ @Bean public CustomRealm customRealm() { CustomRealm customRealm = new CustomRealm(); //因為資料庫密碼存的是明文,所以無需使用雙重md5校驗 // customRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return customRealm; } /** * 密碼驗證器,雙重md5 * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); //設定散列演算法,使用md5演算法 hashedCredentialsMatcher.setHashAlgorithmName("md5"); //散列次數,使用2次md5演算法,相當于md5(md5(xxx)) hashedCredentialsMatcher.setHashIterations(2); return hashedCredentialsMatcher; } /** * 自定義SessionManager * * @return */ @Bean public SessionManager sessionManager() { CustomSessionManager customSessionManager = new CustomSessionManager(); //超時時間,默認 30分鐘,會話超時,單位毫秒 // customSessionManager.setGlobalSessionTimeout(200000); return customSessionManager; } }ShiroConfig.java
package com.ybchen.springboot_shiro.controller; import com.ybchen.springboot_shiro.utils.JsonData; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Arrays; import java.util.List; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 7:22 下午 * @Versiion:1.0 */ @RestController @RequestMapping("admin") public class AdminController { @GetMapping("/video/video_list") public JsonData videoList() { List<String> list = Arrays.asList("docker", "k8s", "jenkins"); return JsonData.buildSuccess(list); } }AdminController.java
package com.ybchen.springboot_shiro.controller; import org.springframework.web.bind.annotation.RestController; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 10:01 下午 * @Versiion:1.0 */ @RestController public class LogoutController { // /** // * 退出,沒必要能這個,退出時,前端直接將token清空即可 // * 還需要獲取前端傳來的token,然后從shiro從清空指定的session_id // * @return // */ // @GetMapping("logout") // public JsonData logout(){ // Subject subject= SecurityUtils.getSubject(); // subject.logout(); // return JsonData.buildSuccess("退出成功"); // } }LogoutController.java
package com.ybchen.springboot_shiro.controller; import com.ybchen.springboot_shiro.utils.JsonData; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 6:28 下午 * @Versiion:1.0 */ @RestController @RequestMapping("authc") public class OrderController { /** * 購買記錄 * @return */ @GetMapping("/video/play_record") public JsonData findMyPlayRecord(){ Map<String,String> recordMap=new HashMap<>(); recordMap.put("1","SpringBoot"); recordMap.put("2","SpringMvc"); return JsonData.buildSuccess(recordMap); } }OrderController.java
package com.ybchen.springboot_shiro.controller; import com.ybchen.springboot_shiro.utils.JsonData; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 9:20 下午 * @Versiion:1.0 */ @RestController public class OtherController { @GetMapping("a") public JsonData a(){ return JsonData.buildSuccess("ok"); } }OtherController.java
package com.ybchen.springboot_shiro.controller; import com.ybchen.springboot_shiro.domain.UserQuery; import com.ybchen.springboot_shiro.utils.JsonData; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 1:12 上午 * @Versiion:1.0 */ @RestController @RequestMapping("pub") public class PublicController { /** * 需要登錄 * * @return */ @GetMapping("need_login") public JsonData needLogin() { return JsonData.buildSuccess(-1, "溫馨提示:請使用對應的賬號登錄"); } /** * 沒權限 * * @return */ @GetMapping("not_permit") public JsonData notPermit() { return JsonData.buildSuccess(-1, "溫馨提示:拒絕訪問,沒權限"); } /** * 首頁 * * @return */ @GetMapping("index") public JsonData index() { List<String> list = Arrays.asList("SpringBoot", "SpringMvc", "Mysql", "Redis"); return JsonData.buildSuccess(list); } /** * 登錄介面 * * @param userQuery * @param request * @param response * @return */ @PostMapping("login") public JsonData login(@RequestBody UserQuery userQuery, HttpServletRequest request, HttpServletResponse response) { //拿到主體 Subject subject = SecurityUtils.getSubject(); try { UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userQuery.getUserName(), userQuery.getPassword()); subject.login(usernamePasswordToken); Map<String,Object> info=new HashMap<>(); info.put("msg","登錄成功"); info.put("session_id",subject.getSession().getId()); return JsonData.buildSuccess(info); }catch (Exception e){ e.printStackTrace(); return JsonData.buildError("賬號或密碼錯誤"); } } }PublicController.java
package com.ybchen.springboot_shiro.controller; import com.ybchen.springboot_shiro.utils.JsonData; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 9:41 下午 * @Versiion:1.0 */ @RestController @RequestMapping("video") public class VideoController { @GetMapping("update") public JsonData updateVideo() { return JsonData.buildSuccess("更新成功"); } @GetMapping("add") public JsonData add(){ return JsonData.buildSuccess("添加成功"); } }VideoController.java
package com.ybchen.springboot_shiro.dao; import com.ybchen.springboot_shiro.domain.Permission; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; /** * 權限 */ public interface PermissionMapper { /** * 根據roleId查詢所有權限 * @param roleId * @return */ @Select("select p.id id,p.name name,p.url url from role_permission rp " + "left join permission p on rp.permission_id=p.id " + "where rp.role_id=#{roleId}") List<Permission> findByPermissionListByRoleId(@Param("roleId") int roleId); }PermissionMapper.java
package com.ybchen.springboot_shiro.dao; import com.ybchen.springboot_shiro.domain.Role; import org.apache.ibatis.annotations.*; import org.apache.ibatis.mapping.FetchType; import java.util.List; /** * 角色 */ public interface RoleMapper { /** * 根據用戶查詢所有的角色 * * @param userId 用戶id * @return */ @Select("select r.id id,r.name name,r.description description from user_role ur " + "left join role r on ur.role_id=r.id " + "where ur.user_id=#{userId}") @Results( value = { @Result(id = true, property = "id", column = "id"), @Result(property = "name", column = "name"), @Result(property = "description", column = "description"), @Result(property = "permissionList", column = "id", many = @Many(select = "com.ybchen.springboot_shiro.dao.PermissionMapper.findByPermissionListByRoleId", fetchType = FetchType.DEFAULT)) } ) List<Role> findRoleListByUserId(@Param("userId") int userId); }RoleMapper.java
package com.ybchen.springboot_shiro.dao; import com.ybchen.springboot_shiro.domain.User; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; /** * 用戶 */ public interface UserMapper { /** * 根據用戶名查詢用戶 * * @param userName 用戶名 * @return */ @Select("select * from user where username=#{userName}") User findByUserName(@Param("userName") String userName); /** * 根據主鍵查詢用戶 * * @param id 主鍵 * @return */ @Select("select * from user where id=#{userId}") User findById(@Param("userId") int id); /** * 根據用戶名和密碼查詢用戶 * * @param userName 用戶名 * @param password 密碼 * @return */ @Select("select * from user where userName=#{userName} and password=#{password}") User findByUserNameAndPassword(@Param("userName") String userName, @Param("password") String password); }UserMapper.java
package com.ybchen.springboot_shiro.domain; /** * @Description:權限 * @Author:chenyanbin * @Date:2021/1/2 11:47 下午 * @Versiion:1.0 */ public class Permission { /** * 主鍵 */ private int id; /** * 權限名稱 */ private String name; /** * 路徑 */ private String url; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } @Override public String toString() { return "Permission{" + "id=" + id + ", name='" + name + '\'' + ", url='" + url + '\'' + '}'; } }Permission.java
package com.ybchen.springboot_shiro.domain; import java.util.ArrayList; import java.util.List; /** * @Description:角色 * @Author:chenyanbin * @Date:2021/1/2 11:43 下午 * @Versiion:1.0 */ public class Role { /** * 主鍵 */ private int id; /** * 角色名稱 */ private String name; /** * 描述 */ private String description; /** * 權限集合 */ private List<Permission> permissionList=new ArrayList<>(); public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public List<Permission> getPermissionList() { return permissionList; } public void setPermissionList(List<Permission> permissionList) { this.permissionList = permissionList; } }Role.java
package com.ybchen.springboot_shiro.domain; /** * @Description:角色權限 * @Author:chenyanbin * @Date:2021/1/2 11:44 下午 * @Versiion:1.0 */ public class RolePermission { /** * 主鍵 */ private int id; /** * 角色id */ private int roleId; /** * 權限id */ private int permissiionId; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getRoleId() { return roleId; } public void setRoleId(int roleId) { this.roleId = roleId; } public int getPermissiionId() { return permissiionId; } public void setPermissiionId(int permissiionId) { this.permissiionId = permissiionId; } @Override public String toString() { return "RolePermission{" + "id=" + id + ", roleId=" + roleId + ", permissiionId=" + permissiionId + '}'; } }RolePermission.java
package com.ybchen.springboot_shiro.domain; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @Description:用戶表 * @Author:chenyanbin * @Date:2021/1/2 11:41 下午 * @Versiion:1.0 */ public class User { /** * 主鍵 */ private int id; /** * 用戶名 */ private String username; /** * 密碼 */ private String password; /** * 創建時間 */ private Date createTime; /** * 密碼加鹽 */ private String salt; /** * 角色集合 */ private List<Role> roleList=new ArrayList<>(); public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getSalt() { return salt; } public void setSalt(String salt) { this.salt = salt; } public List<Role> getRoleList() { return roleList; } public void setRoleList(List<Role> roleList) { this.roleList = roleList; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + ", salt='" + salt + '\'' + ", roleList=" + roleList + '}'; } }User.java
package com.ybchen.springboot_shiro.domain; import java.io.Serializable; /** * @Description:接收用戶名和密碼 * @Author:chenyanbin * @Date:2021/1/3 6:19 下午 * @Versiion:1.0 */ public class UserQuery implements Serializable { private String userName; private String password; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "UserQuery{" + "userName='" + userName + '\'' + ", password='" + password + '\'' + '}'; } }UserQuery.java
package com.ybchen.springboot_shiro.domain; /** * @Description:用戶角色 * @Author:chenyanbin * @Date:2021/1/2 11:46 下午 * @Versiion:1.0 */ public class UserRole { /** * 主鍵 */ private int id; /** * 角色id */ private int roleId; /** * 用戶id */ private int userId; /** * 備注 */ private String remark; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getRoleId() { return roleId; } public void setRoleId(int roleId) { this.roleId = roleId; } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } @Override public String toString() { return "UserRole{" + "id=" + id + ", roleId=" + roleId + ", userId=" + userId + ", remark='" + remark + '\'' + '}'; } }UserRole.java
package com.ybchen.springboot_shiro.exception; /** * @Description:自定義例外 * @Author:chenyanbin * @Date:2021/1/3 7:31 下午 * @Versiion:1.0 */ public class CustomException extends RuntimeException{ private Integer code; private String msg; public CustomException(Integer code, String msg) { this.code = code; this.msg = msg; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } @Override public String toString() { return "CustomException{" + "code=" + code + ", msg='" + msg + '\'' + '}'; } }CustomException.java
package com.ybchen.springboot_shiro.exception; import com.ybchen.springboot_shiro.utils.JsonData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; /** * @ClassName:GlobalExceptiions * @Description:TODO * @Author:chenyb * @Date:2020/12/9 11:34 上午 * @Versiion:1.0 */ @ControllerAdvice public class GlobalExceptiions { private final Logger logger = LoggerFactory.getLogger(getClass()); @ExceptionHandler(value = Exception.class) @ResponseBody public JsonData handle(Exception ex) { logger.info("[ 全域例外 ] ===============》 {}", ex); if (ex instanceof CustomException) { CustomException customException = (CustomException) ex; return JsonData.buildError(customException.getCode(), customException.getMsg()); } return JsonData.buildError("系統內部錯誤,請聯系管理員!"); } }GlobalExceptiions.java
package com.ybchen.springboot_shiro.service.impl; import com.ybchen.springboot_shiro.dao.RoleMapper; import com.ybchen.springboot_shiro.dao.UserMapper; import com.ybchen.springboot_shiro.domain.Role; import com.ybchen.springboot_shiro.domain.User; import com.ybchen.springboot_shiro.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 1:15 上午 * @Versiion:1.0 */ @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private RoleMapper roleMapper; @Override public User findAllUserInfoByUserName(String userName) { User user = userMapper.findByUserName(userName); //用戶角色的集合 List<Role> roleList = roleMapper.findRoleListByUserId(user.getId()); user.setRoleList(roleList); return user; } @Override public User findSimpleUserInfoById(int userId) { return userMapper.findById(userId); } @Override public User findSimpleUserInfoByUserName(String userName) { return userMapper.findByUserName(userName); } }UserServiceImpl.java
package com.ybchen.springboot_shiro.service; import com.ybchen.springboot_shiro.domain.User; public interface UserService { /** * 獲取全部用戶資訊,包括角色、權限 * @param userName * @return */ User findAllUserInfoByUserName(String userName); /** * 獲取用戶基本資訊 * @param userId * @return */ User findSimpleUserInfoById(int userId); /** * 根據用戶名查詢用戶資訊 * @param userName * @return */ User findSimpleUserInfoByUserName(String userName); }UserService.java
package com.ybchen.springboot_shiro.utils; import java.io.Serializable; public class JsonData implements Serializable { private static final long serialVersionUID = 1L; /** * 狀態碼,0表示成功過,1表示處理中,-1表示失敗 */ private Integer code; /** * 業務資料 */ private Object data; /** * 資訊描述 */ private String msg; public JsonData() { } public JsonData(Integer code, Object data, String msg) { this.code = code; this.data =https://www.cnblogs.com/chenyanbin/p/ data; this.msg = msg; } /** * 成功,不用回傳資料 * * @return */ public static JsonData buildSuccess() { return new JsonData(0, null, null); } /** * 成功,回傳資料 * * @param data 回傳資料 * @return */ public static JsonData buildSuccess(Object data) { return new JsonData(0, data, null); } /** * 成功,回傳資料 * * @param code 狀態碼 * @param data 回傳資料 * @return */ public static JsonData buildSuccess(int code, Object data) { return new JsonData(code, data, null); } /** * 失敗,回傳資訊 * * @param msg 回傳資訊 * @return */ public static JsonData buildError(String msg) { return new JsonData(-1, null, msg); } /** * 失敗,回傳資訊和狀態碼 * * @param code 狀態碼 * @param msg 回傳資訊 * @return */ public static JsonData buildError(Integer code, String msg) { return new JsonData(code, null, msg); } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public Object getData() { return data; } public void setData(Object data) { this.data =https://www.cnblogs.com/chenyanbin/p/ data; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } @Override public String toString() { return "JsonData{" + "code=" + code + ", data="https://www.cnblogs.com/chenyanbin/p/+ data +", msg='" + msg + '\'' + '}'; } }JsonData.java
package com.ybchen.springboot_shiro; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //掃描mapper @MapperScan(value = "https://www.cnblogs.com/chenyanbin/p/com.ybchen.springboot_shiro.dao") public class SpringbootShiroApplication { public static void main(String[] args) { SpringApplication.run(SpringbootShiroApplication.class, args); } }SpringbootShiroApplication.java
server.port=12888 #============資料庫================= spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shiro_2?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=root # 使用阿里巴巴druid資料源,默認使用自帶的 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource # 開啟控制臺列印sql mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # mybatis下劃線轉駝峰配置 mybatis.configuration.map-underscore-to-camel-case=trueapplication.properties
package com.ybchen.springboot_shiro; import org.apache.shiro.crypto.hash.SimpleHash; import org.junit.Test; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 10:12 下午 * @Versiion:1.0 */ public class Md5Test { @Test public void testMd5(){ String hashName="md5"; String pwd="123"; SimpleHash simpleHash = new SimpleHash(hashName, pwd, null, 2); System.out.println(simpleHash); } }Md5Test.java
<?xml version="1.0" encoding="UTF-8"?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.ybchen</groupId> <artifactId>springboot_shiro</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot_shiro</name> <description>SpringBoot整合Shiro</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.3</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.0</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>pom.xml
專案原始碼
鏈接: https://pan.baidu.com/s/1adjwICKge83YcPycE8ZaEQ 密碼: if9s
專案postman測驗
127.0.0.1:12888/pub/index
127.0.0.1:12888/pub/not_permit
127.0.0.1:12888/pub/need_login
127.0.0.1:12888/pub/login
127.0.0.1:12888/authc/video/play_record
127.0.0.1:12888/admin/video/video_list
127.0.0.1:12888/video/add
127.0.0.1:12888/video/update

備注
因為鏈接較多,就不一一做gif動圖了,直接匯入專案原始碼,請求的時候,在header上加入token即可~
Filter過濾器
業務需求
- 一個介面,可以讓2個角色中的任意一個訪問
- 自定義一個類,繼承:AuthorizationFilter
package com.ybchen.springboot_shiro.config; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.CollectionUtils; import org.apache.shiro.web.filter.authz.AuthorizationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.util.Set; /** * @Description:自定義Filter * @Author:chenyanbin * @Date:2021/1/4 11:14 下午 * @Versiion:1.0 */ public class CustomRolesOrAuthorizationFilter extends AuthorizationFilter { @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { Subject subject = getSubject(request, response); //filterChainDefinitionMap.put("/admin/**", "roles[admin,user]"); mappedValue <==> admin,user String[] rolesArray = (String[]) mappedValue; if (rolesArray == null || rolesArray.length == 0) { return true; } Set<String> roles = CollectionUtils.asSet(rolesArray); //當前subject是roles中的任意一個,則有權限訪問 for (String role : roles) { if (subject.hasRole(role)) { return true; } } return false; } }

@Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { System.out.println("ShiroConfig ShiroFilterFactoryBean 執行"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //設定SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //如果訪問需要登錄的某個介面,卻沒有登錄,則呼叫此介面(如果不是前后端分離,則跳轉頁面) shiroFilterFactoryBean.setLoginUrl("/pub/need_login"); //shiroFilterFactoryBean.setLoginUrl("/xxx.jsp"); //登錄成功后,跳轉的鏈接,若前后端分離,沒必要設定這個 //shiroFilterFactoryBean.setSuccessUrl(""); //登錄成功,未授權會呼叫此方法 shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_permit"); //設定自定義Filter Map<String, Filter> filterMap=new LinkedHashMap<>(); filterMap.put("roleOrFilter",new CustomRolesOrAuthorizationFilter()); shiroFilterFactoryBean.setFilters(filterMap); //攔截路徑,必須使用:LinkedHashMap,要不然攔截效果會時有時無,因為使用的是無序的Map Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //key=正則運算式路徑,value=https://www.cnblogs.com/chenyanbin/p/org.apache.shiro.web.filter.mgt.DefaultFilter //退出過濾器 filterChainDefinitionMap.put("/logout", "logout"); //匿名可以訪問,游客模式 filterChainDefinitionMap.put("/pub/**", "anon"); //登錄用戶才可以訪問 filterChainDefinitionMap.put("/authc/**", "authc"); //管理員角色才能訪問 // filterChainDefinitionMap.put("/admin/**", "roles[admin,user]"); filterChainDefinitionMap.put("/admin/**", "roleOrFilter[admin,user]"); //有編輯權限才能訪問 filterChainDefinitionMap.put("/video/update", "perms[video_update]"); //authc:url必須通過認證才可以訪問 //anon:url可以匿名訪問 //過濾鏈是順序執行,從上而下,一般把/**,放到最下面 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
Redis整合CacheManager
原因
授權的時候每次都去查詢資料庫,對于頻繁訪問的介面,性能和回應速度比較慢,此處可以使用快取,提高回應速度,也可以使用Guava(本地記憶體快取),
Redis(分布式快取)還不了解的小伙伴,在這里我就不一一講解了,可以看我以前寫過的博客,
- Redis 從入門到精通:點我直達
- Redis 微信搶紅包,電商場景下秒殺系統設計:點我直達
- Redis 高級專案實戰:點我直達
添加依賴
<!--shiro+redis--> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>3.3.1</version> </dependency>
在ShiroConfig中添加如下代碼
//使用自定義cacheManager securityManager.setCacheManager(cacheManager()); /** * 配置redisManager * @return */ public RedisManager getRedisManager(){ RedisManager redisManager=new RedisManager(); redisManager.setHost("127.0.0.1:6379"); //連接那個資料庫 redisManager.setDatabase(0); //設定密碼 // redisManager.setPassword("123"); return redisManager; } /** * 設定具體cache實作類 * @return */ public RedisCacheManager cacheManager(){ RedisCacheManager redisCacheManager=new RedisCacheManager(); redisCacheManager.setRedisManager(getRedisManager()); return redisCacheManager; }

修改CustomRealm

設定redis快取過期時間

Redis整合SessionManager
為啥Session也要持久化
重啟應用,用戶無感知,可以繼續以原先的狀態繼續訪問,
修改shiroconfig

package com.ybchen.springboot_shiro.config; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.crazycake.shiro.RedisCacheManager; import org.crazycake.shiro.RedisManager; import org.crazycake.shiro.RedisSessionDAO; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.Map; /** * @Description: * @Author:chenyanbin * @Date:2021/1/3 4:12 下午 * @Versiion:1.0 */ @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { System.out.println("ShiroConfig ShiroFilterFactoryBean 執行"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //設定SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //如果訪問需要登錄的某個介面,卻沒有登錄,則呼叫此介面(如果不是前后端分離,則跳轉頁面) shiroFilterFactoryBean.setLoginUrl("/pub/need_login"); //shiroFilterFactoryBean.setLoginUrl("/xxx.jsp"); //登錄成功后,跳轉的鏈接,若前后端分離,沒必要設定這個 //shiroFilterFactoryBean.setSuccessUrl(""); //登錄成功,未授權會呼叫此方法 shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_permit"); //設定自定義Filter Map<String, Filter> filterMap=new LinkedHashMap<>(); filterMap.put("roleOrFilter",new CustomRolesOrAuthorizationFilter()); shiroFilterFactoryBean.setFilters(filterMap); //攔截路徑,必須使用:LinkedHashMap,要不然攔截效果會時有時無,因為使用的是無序的Map Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //key=正則運算式路徑,value=https://www.cnblogs.com/chenyanbin/p/org.apache.shiro.web.filter.mgt.DefaultFilter //退出過濾器 filterChainDefinitionMap.put("/logout", "logout"); //匿名可以訪問,游客模式 filterChainDefinitionMap.put("/pub/**", "anon"); //登錄用戶才可以訪問 filterChainDefinitionMap.put("/authc/**", "authc"); //管理員角色才能訪問 // filterChainDefinitionMap.put("/admin/**", "roles[admin,user]"); filterChainDefinitionMap.put("/admin/**", "roleOrFilter[admin,user]"); //有編輯權限才能訪問 filterChainDefinitionMap.put("/video/update", "perms[video_update]"); //authc:url必須通過認證才可以訪問 //anon:url可以匿名訪問 //過濾鏈是順序執行,從上而下,一般把/**,放到最下面 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //如果不是前后端分離,不用設定setSessionManager securityManager.setSessionManager(sessionManager()); //使用自定義cacheManager securityManager.setCacheManager(cacheManager()); securityManager.setRealm(customRealm()); return securityManager; } /** * 配置redisManager * @return */ public RedisManager getRedisManager(){ RedisManager redisManager=new RedisManager(); redisManager.setHost("127.0.0.1:6379"); //連接那個資料庫 redisManager.setDatabase(0); //設定密碼 // redisManager.setPassword("123"); return redisManager; } /** * 設定具體cache實作類 * @return */ public RedisCacheManager cacheManager(){ RedisCacheManager redisCacheManager=new RedisCacheManager(); redisCacheManager.setRedisManager(getRedisManager()); //設定快取過期時間 redisCacheManager.setExpire(20); return redisCacheManager; } /** * 自定義realm * * @return */ @Bean public CustomRealm customRealm() { CustomRealm customRealm = new CustomRealm(); //因為資料庫密碼存的是明文,所以無需使用雙重md5校驗 // customRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return customRealm; } /** * 密碼驗證器,雙重md5 * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); //設定散列演算法,使用md5演算法 hashedCredentialsMatcher.setHashAlgorithmName("md5"); //散列次數,使用2次md5演算法,相當于md5(md5(xxx)) hashedCredentialsMatcher.setHashIterations(2); return hashedCredentialsMatcher; } /** * 自定義SessionManager * * @return */ @Bean public SessionManager sessionManager() { CustomSessionManager customSessionManager = new CustomSessionManager(); //超時時間,默認 30分鐘,會話超時,單位毫秒 // customSessionManager.setGlobalSessionTimeout(200000); //配置session持久化 customSessionManager.setSessionDAO(redisSessionDAO()); return customSessionManager; } /** * 自定義session持久化 * @return */ public RedisSessionDAO redisSessionDAO(){ RedisSessionDAO redisSessionDAO=new RedisSessionDAO(); redisSessionDAO.setRedisManager(getRedisManager()); return redisSessionDAO; } }ShiroConfig.java
Shiro整合Redis后的原始碼
鏈接: https://pan.baidu.com/s/1cNQfBiw50A-U5izzOQclpw 密碼: 6wqt
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/245040.html
標籤:Java
