簡(jiǎn)介
Apache Shiro是一個(gè)強(qiáng)大且易用的Java安全框架,執(zhí)行身份驗(yàn)證、授權(quán)、密碼學(xué)和會(huì)話管理。使用Shiro的易于理解的API,您可以快速、輕松地獲得任何應(yīng)用程序,從最小的移動(dòng)應(yīng)用程序到最大的網(wǎng)絡(luò)和企業(yè)應(yīng)用程序
Shiro的三個(gè)核心組件:Subject,缰贝、SecurityManager 以及 Realms
Subject:即“當(dāng)前操作用戶”。但是畔濒,在Shiro中剩晴,Subject這一概念并不僅僅指人,也可以是第三方進(jìn)程侵状、后臺(tái)帳戶(Daemon Account)或其他類似事物赞弥。它僅僅意味著“當(dāng)前跟軟件交互的東西”。但考慮到大多數(shù)目的和用途趣兄,你可以把它認(rèn)為是Shiro的“用戶”概念绽左。Subject代表了當(dāng)前用戶的安全操作,SecurityManager則管理所有用戶的安全操作
SecurityManager:它是Shiro框架的核心艇潭,典型的Facade模式拼窥,Shiro通過(guò)SecurityManager來(lái)管理內(nèi)部組件實(shí)例戏蔑,并通過(guò)它來(lái)提供安全管理的各種服務(wù)
Realm: Realm充當(dāng)了Shiro與應(yīng)用安全數(shù)據(jù)間的“橋梁”或者“連接器”。也就是說(shuō)鲁纠,當(dāng)對(duì)用戶執(zhí)行認(rèn)證(登錄)和授權(quán)(訪問(wèn)控制)驗(yàn)證時(shí)总棵,Shiro會(huì)從應(yīng)用配置的Realm中查找用戶及其權(quán)限信息。從這個(gè)意義上講改含,Realm實(shí)質(zhì)上是一個(gè)安全相關(guān)的DAO:它封裝了數(shù)據(jù)源的連接細(xì)節(jié)情龄,并在需要時(shí)將相關(guān)數(shù)據(jù)提供給Shiro。當(dāng)配置Shiro時(shí)捍壤,你必須至少指定一個(gè)Realm骤视,用于認(rèn)證和(或)授權(quán)。配置多個(gè)Realm是可以的白群,但是至少需要一個(gè)尚胞。
注:上面的介紹參考至百度百科
Shiro的認(rèn)證流程以及基本概念介紹
(1)第一個(gè)入門實(shí)例:
/**
* 第一個(gè)實(shí)例
* 參考:http://jinnianshilongnian.iteye.com/blog/2019547
* */
@Test
public void testHello(){
//1 獲取SecurityManager工廠
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:cn/zifangsky/test/shiro/base/shiro.ini");
//2 獲取SecurityManager實(shí)例硬霍,并綁定給SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//3 獲取Subject帜慢,創(chuàng)建用戶名/密碼驗(yàn)證Token
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
try {
//4 登錄,即:身份認(rèn)證
subject.login(token);
System.out.println("登錄認(rèn)證成功");
} catch (AuthenticationException e) {
//5 認(rèn)證失敗
System.err.println("認(rèn)證失敗");
}
//6 退出登錄
subject.logout();
}
注:這里為了方便使用了JUnit單元測(cè)試唯卖。也就是說(shuō)需要在此次測(cè)試的項(xiàng)目中引入junit-4.10.jar
這個(gè)jar包粱玲,同時(shí)需要在你的類上添加@RunWith(JUnit4.class)
這個(gè)注解,比如說(shuō)這樣:
@RunWith(JUnit4.class)
public class Helloworld {
}
同時(shí)拜轨,上面用到的shiro.ini這個(gè)配置文件是這樣的:
[users]
admin=admin
test=123456
(2)自定義Realm:
關(guān)于Realm這個(gè)類抽减,當(dāng)需要對(duì)用戶執(zhí)行認(rèn)證(登錄)和授權(quán)(訪問(wèn)控制)驗(yàn)證時(shí),Shiro都會(huì)從應(yīng)用配置的Realm中查找用戶信息以及權(quán)限信息橄碾。因此卵沉,我們可以自定義一個(gè)Realm以實(shí)現(xiàn)自定義登錄認(rèn)證和權(quán)限控制,下面我將用一個(gè)簡(jiǎn)單例子介紹通過(guò)自定義Realm實(shí)現(xiàn)自定義認(rèn)證(PS:關(guān)于自定義授權(quán)相關(guān)內(nèi)容放到下一篇文章中敘述):
i)自定義的CustomRealm.java:
package cn.zifangsky.test.shiro.base;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;
public class CustomRealm implements Realm {
/**
* 返回一個(gè)唯一的Realm名稱
* */
@Override
public String getName() {
return "CustomRealm";
}
/**
* 判斷此Realm是否支持此Token
* */
@Override
public boolean supports(AuthenticationToken token) {
//僅支持UsernamePasswordToken類型的Token
return token instanceof UsernamePasswordToken;
}
/**
* 自定義認(rèn)證
* */
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//根據(jù)token獲取需要認(rèn)證的信息(登錄時(shí)輸入的信息)
String username = (String) token.getPrincipal(); //獲取用戶名
String password = String.valueOf((char[])token.getCredentials()); //獲取密碼
if(!"test".equals(username))
throw new UnknownAccountException();
if(!"123456".equals(password))
throw new IncorrectCredentialsException();
return new SimpleAuthenticationInfo(username, password, getName());
}
}
ii)對(duì)應(yīng)的配置文件shiro-realm.ini:
#申明一個(gè)自定義的Realm
customRealm=cn.zifangsky.test.shiro.base.CustomRealm
#指定securityManager的realms實(shí)現(xiàn)
securityManager.realms=$customRealm
iii)測(cè)試方法:
/**
* 測(cè)試自定義Realm
* */
@Test
public void testCustomRealm(){
//1 獲取SecurityManager工廠
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:cn/zifangsky/test/shiro/base/shiro-realm.ini");
//2 獲取SecurityManager實(shí)例法牲,并綁定給SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//3 獲取Subject史汗,創(chuàng)建用戶名/密碼驗(yàn)證Token
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("test", "123456");
try {
//4 登錄,即:身份認(rèn)證
subject.login(token);
System.out.println("登錄認(rèn)證成功");
} catch (AuthenticationException e) {
//5 認(rèn)證失敗
System.err.println("認(rèn)證失敗");
}
//6 退出登錄
subject.logout();
}
關(guān)于這個(gè)測(cè)試方法拒垃,除了使用了一個(gè)不同的ini配置文件之外停撞,其余部分都跟上面那個(gè)例子是一樣的,因此這里就不多說(shuō)了.
(3)使用數(shù)據(jù)庫(kù)的shiro登錄認(rèn)證:
在上面的兩個(gè)例子中都沒(méi)有使用到數(shù)據(jù)庫(kù)悼瓮,同時(shí)登錄認(rèn)證的賬號(hào)密碼都硬編碼地配置到ini配置文件中了戈毒。但是實(shí)際的開(kāi)發(fā)中是不可能將用戶信息這樣硬編碼的,因此接下來(lái)我將介紹基于數(shù)據(jù)庫(kù)的shiro登錄認(rèn)證:
i)測(cè)試使用的SQL語(yǔ)句:
我在這里測(cè)試時(shí)使用的數(shù)據(jù)庫(kù)是MySQL横堡,測(cè)試使用的SQL語(yǔ)句是:
-- ----------------------------
-- Table structure for roles_permissions
-- ----------------------------
DROP TABLE IF EXISTS `roles_permissions`;
CREATE TABLE `roles_permissions` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_name` varchar(100) DEFAULT NULL,
`permission` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_roles_permissions` (`role_name`,`permission`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of roles_permissions
-- ----------------------------
INSERT INTO `roles_permissions` VALUES ('1', 'admin', '/');
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
`password_salt` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_users_username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('1', 'admin', '123456', null);
-- ----------------------------
-- Table structure for user_roles
-- ----------------------------
DROP TABLE IF EXISTS `user_roles`;
CREATE TABLE `user_roles` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`role_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_roles` (`username`,`role_name`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_roles
-- ----------------------------
INSERT INTO `user_roles` VALUES ('1', 'admin', 'admin');
注:在基于ini配置文件的配置時(shí)埋市,如果不手動(dòng)改寫shiro的查詢語(yǔ)句,那么shiro在進(jìn)行認(rèn)證和授權(quán)時(shí)默認(rèn)會(huì)查詢上面的這幾個(gè)表
ii)配置文件shiro-jdbc-realm.ini:
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=root
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm
這里的配置也很簡(jiǎn)單命贴,先是定義了一個(gè)JDBC的Realm道宅,接著定義了一個(gè)數(shù)據(jù)源以及它的一些基本配置腊满。需要注意的是我這里使用的是druid連接池,如果要使用這個(gè)連接池的話需要引入druid-1.0.26.jar這個(gè)jar包培己。當(dāng)然碳蛋,這里也可以使用其他的一些連接池或者基本的jdbc數(shù)據(jù)源
iii)測(cè)試方法:
/**
* 測(cè)試JDBC Realm
* */
@Test
public void testJdbcRealm(){
//1 獲取SecurityManager工廠
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:cn/zifangsky/test/shiro/base/shiro-jdbc-realm.ini");
//2 獲取SecurityManager實(shí)例,并綁定給SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//3 獲取Subject省咨,創(chuàng)建用戶名/密碼驗(yàn)證Token
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
try {
//4 登錄肃弟,即:身份認(rèn)證
subject.login(token);
System.out.println("登錄認(rèn)證成功");
} catch (AuthenticationException e) {
//5 認(rèn)證失敗
System.err.println("認(rèn)證失敗");
}
//6 退出登錄
subject.logout();
}
很顯然,運(yùn)行這個(gè)方法也是可以登錄成功的零蓉,輸出結(jié)果略
(4)用戶擁有的角色、權(quán)限判斷:
package cn.zifangsky.test.shiro.func;
import java.util.Arrays;
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;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class RoleTest {
/**
* 粒度大敌蜂,如果某種角色不存在了則需要?jiǎng)h除所有的用戶對(duì)應(yīng)的角色信息
* */
@Test
public void testHasRole() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:cn/zifangsky/test/shiro/func/shiro-role.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
subject.login(token);
System.out.println(subject.hasRole("role1"));
System.out.println(subject.hasAllRoles(Arrays.asList("role1", "role2")));
boolean[] results = subject.hasRoles(Arrays.asList("role1", "role2", "role3"));
System.out.println("results[0]: " + results[0]);
System.out.println("results[1]: " + results[1]);
System.out.println("results[2]: " + results[2]);
// subject.checkRole("role3");
}
/**
* 粒度小,如果某種角色不存在了只需要?jiǎng)h除該角色即可
* */
@Test
public void testIsPermitted(){
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:cn/zifangsky/test/shiro/func/shiro-permission.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
subject.login(token);
System.out.println(subject.isPermitted("user:create"));
System.out.println(subject.isPermittedAll("user:create","user:delete"));
}
}
這里測(cè)試所用的兩個(gè)ini配置文件分別是:
i)shiro-role.ini:
[users]
admin=admin,role1,role2
test=123456,role1
其格式是:
用戶名=密碼,角色1,角色2 …
ii)shiro-permission.ini:
[users]
admin=admin,role1,role2
test=123456,role1
[roles]
role1=user:create
role2=user:create,user:update
role3=user:create,update,delete
role4=user:*
role5=*:view
這里的格式跟上面的差不多汗贫,不同的是星號(hào)表示任意,例如:role4=user:* 表示role4這個(gè)角色擁有user的所有權(quán)限落包。
上面的例子最后輸出如下:
方法一:
true
true
results[0]: true
results[1]: true
results[2]: false
方法二:
true
false
基于角色的訪問(wèn)控制(Role-Based Access Control)
基于角色的訪問(wèn)控制簡(jiǎn)稱:RBAC摊唇。在本篇文章的末尾補(bǔ)充介紹一點(diǎn)目前最常用也是最基本的權(quán)限控制模型,也就是基于角色的訪問(wèn)控制巷查。
基于RBAC的權(quán)限管理有序,其實(shí)體關(guān)系大致是這樣的:
因?yàn)橛脩襞c角色之間的關(guān)系是多對(duì)多的,角色與權(quán)限之間的關(guān)系也是多對(duì)多的岛请。因此分別建立了用戶角色關(guān)聯(lián)表
旭寿、角色權(quán)限關(guān)聯(lián)表
分別存放用戶與角色之間、角色與權(quán)限之間的對(duì)應(yīng)關(guān)系髓需。測(cè)試的數(shù)據(jù)庫(kù)表如下:
DROP TABLE IF EXISTS `usr_func`;
CREATE TABLE `usr_func` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`description` varchar(100) DEFAULT NULL,
`code` varchar(100) DEFAULT NULL,
`url` varchar(200) DEFAULT NULL,
`status` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of usr_func
-- ----------------------------
INSERT INTO `usr_func` VALUES ('1', '用戶管理-查詢', null, 'YHGL:CX', null, 'enable');
INSERT INTO `usr_func` VALUES ('2', '用戶管理-新增', null, 'YHGL:XZ', null, 'enable');
INSERT INTO `usr_func` VALUES ('3', '用戶管理-編輯', null, 'YHGL:BJ', null, 'enable');
INSERT INTO `usr_func` VALUES ('4', '用戶管理-停用', null, 'YHGL:TY', null, 'enable');
INSERT INTO `usr_func` VALUES ('5', '用戶管理-啟用', null, 'YHGL:QY', null, 'enable');
INSERT INTO `usr_func` VALUES ('6', '用戶管理-刪除', null, 'YHGL:SC', null, 'enable');
INSERT INTO `usr_func` VALUES ('7', '文章管理-查詢', null, 'WZGL:CX', null, 'enable');
INSERT INTO `usr_func` VALUES ('8', '文章管理-新增', null, 'WZGL:XZ', null, 'enable');
INSERT INTO `usr_func` VALUES ('9', '文章管理-編輯', null, 'WZGL:BJ', null, 'enable');
INSERT INTO `usr_func` VALUES ('10', '文章管理-刪除', null, 'WZGL:SC', null, 'enable');
-- ----------------------------
-- Table structure for usr_role
-- ----------------------------
DROP TABLE IF EXISTS `usr_role`;
CREATE TABLE `usr_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`roleName` varchar(100) DEFAULT NULL,
`description` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of usr_role
-- ----------------------------
INSERT INTO `usr_role` VALUES ('1', 'manager', '管理員');
INSERT INTO `usr_role` VALUES ('2', 'editor', '編輯');
INSERT INTO `usr_role` VALUES ('3', 'author', '作者');
INSERT INTO `usr_role` VALUES ('4', 'subscriber', '訂閱者');
INSERT INTO `usr_role` VALUES ('5', 'contributor', '投稿者');
-- ----------------------------
-- Table structure for usr_role_func
-- ----------------------------
DROP TABLE IF EXISTS `usr_role_func`;
CREATE TABLE `usr_role_func` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`roleId` int(11) DEFAULT NULL,
`funcId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `roleId` (`roleId`),
CONSTRAINT `roleId` FOREIGN KEY (`roleId`) REFERENCES `usr_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of usr_role_func
-- ----------------------------
INSERT INTO `usr_role_func` VALUES ('1', '1', '1');
INSERT INTO `usr_role_func` VALUES ('2', '1', '2');
INSERT INTO `usr_role_func` VALUES ('3', '1', '3');
INSERT INTO `usr_role_func` VALUES ('4', '1', '4');
INSERT INTO `usr_role_func` VALUES ('5', '1', '5');
INSERT INTO `usr_role_func` VALUES ('6', '1', '6');
INSERT INTO `usr_role_func` VALUES ('7', '1', '7');
INSERT INTO `usr_role_func` VALUES ('8', '1', '8');
INSERT INTO `usr_role_func` VALUES ('9', '1', '9');
INSERT INTO `usr_role_func` VALUES ('10', '1', '10');
INSERT INTO `usr_role_func` VALUES ('11', '2', '7');
INSERT INTO `usr_role_func` VALUES ('12', '2', '8');
INSERT INTO `usr_role_func` VALUES ('13', '2', '9');
INSERT INTO `usr_role_func` VALUES ('14', '2', '10');
INSERT INTO `usr_role_func` VALUES ('15', '3', '7');
INSERT INTO `usr_role_func` VALUES ('16', '3', '8');
INSERT INTO `usr_role_func` VALUES ('17', '3', '9');
INSERT INTO `usr_role_func` VALUES ('18', '4', '7');
INSERT INTO `usr_role_func` VALUES ('19', '5', '8');
-- ----------------------------
-- Table structure for usr_user
-- ----------------------------
DROP TABLE IF EXISTS `usr_user`;
CREATE TABLE `usr_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`password` varchar(256) DEFAULT NULL,
`mobile` varchar(30) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`createTime` datetime DEFAULT NULL,
`updateTime` datetime DEFAULT NULL,
`channelId` int(11) DEFAULT NULL,
`status` varchar(20) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of usr_user
-- ----------------------------
INSERT INTO `usr_user` VALUES ('1', 'admin', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', '110', 'admin@zifangsky.cn', '2016-10-04 10:33:23', '2016-10-06 10:38:40', '1', 'enable');
INSERT INTO `usr_user` VALUES ('2', 'test', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', '3456789', 'test@110.com', '2016-10-18 18:25:12', '2016-10-19 18:25:17', '2', 'enable');
INSERT INTO `usr_user` VALUES ('5', 'zifangsky', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', '911', 'admin@zifangsky.cn', '2016-10-20 11:46:45', '2016-10-20 11:46:57', '1', 'enable');
INSERT INTO `usr_user` VALUES ('6', 'sub', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', null, null, null, null, null, 'disable');
INSERT INTO `usr_user` VALUES ('7', 'contributor', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', null, null, null, null, null, 'disable');
-- ----------------------------
-- Table structure for usr_user_role
-- ----------------------------
DROP TABLE IF EXISTS `usr_user_role`;
CREATE TABLE `usr_user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userId` int(11) DEFAULT NULL,
`roleId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `userId` (`userId`),
CONSTRAINT `userId` FOREIGN KEY (`userId`) REFERENCES `usr_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of usr_user_role
-- ----------------------------
INSERT INTO `usr_user_role` VALUES ('1', '1', '1');
INSERT INTO `usr_user_role` VALUES ('2', '5', '3');
INSERT INTO `usr_user_role` VALUES ('3', '5', '5');
INSERT INTO `usr_user_role` VALUES ('4', '2', '4');
INSERT INTO `usr_user_role` VALUES ('5', '6', '2');
最后的測(cè)試代碼如下:
package cn.zifangsky.test.shiro.func;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
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;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import junit.framework.Assert;
@RunWith(JUnit4.class)
public class RoleFuncTest {
@Test
public void test(){
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:cn/zifangsky/test/shiro/func/shiro-jdbc-func.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("admin", "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918");
try {
subject.login(token);
Assert.assertEquals(true, subject.isAuthenticated());
//判斷用戶是否擁有某個(gè)角色
System.out.println(subject.hasRole("manager")); //true
System.out.println(subject.hasRole("editor")); //false
//判斷是否被授權(quán)
System.out.println(subject.isPermitted("YHGL:CX")); //true
System.out.println(subject.isPermitted("YHGL:XZ")); //true
subject.logout();
} catch (IncorrectCredentialsException e) {
System.out.println("登錄密碼錯(cuò)誤. Password for account " + token.getPrincipal() + " was incorrect.");
} catch (ExcessiveAttemptsException e) {
System.out.println("登錄失敗次數(shù)過(guò)多");
} catch (LockedAccountException e) {
System.out.println("帳號(hào)已被鎖定. The account for username " + token.getPrincipal() + " was locked.");
} catch (DisabledAccountException e) {
System.out.println("帳號(hào)已被禁用. The account for username " + token.getPrincipal() + " was disabled.");
} catch (ExpiredCredentialsException e) {
System.out.println("帳號(hào)已過(guò)期. the account for username " + token.getPrincipal() + " was expired.");
} catch (UnknownAccountException e) {
System.out.println("帳號(hào)不存在. There is no user with username of " + token.getPrincipal());
}
}
}
對(duì)應(yīng)的配置文件shiro-jdbc-func.ini:
[main]
dataSource=org.springframework.jdbc.datasource.DriverManagerDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://127.0.0.1:3306/rbac_db
dataSource.username=root
dataSource.password=root
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.dataSource=$dataSource
#用戶認(rèn)證(登錄)查詢語(yǔ)句许师,以用戶名為查詢條件
jdbcRealm.authenticationQuery = SELECT password FROM usr_user WHERE username = ?
#用戶角色查詢語(yǔ)句,以用戶名為查詢條件僚匆,判斷用戶是否擁有某個(gè)角色
jdbcRealm.userRolesQuery = SELECT usr_role.roleName from usr_user,usr_user_role,usr_role WHERE usr_user.username = ? AND usr_user.id = usr_user_role.userId AND usr_user_role.roleId = usr_role.id
#資源許可查詢語(yǔ)句微渠,以角色名稱為查詢條件,判斷角色是否擁有某個(gè)資源的許可
jdbcRealm.permissionsQuery = SELECT usr_func.code from usr_role,usr_role_func,usr_func WHERE usr_role.roleName = ? AND usr_role.id = usr_role_func.roleId AND usr_role_func.funcId = usr_func.id
securityManager.realms=$jdbcRealm
關(guān)于這個(gè)配置文件中定義的大部分內(nèi)容都已經(jīng)在上面的例子中說(shuō)過(guò)了咧擂,唯一新添加的內(nèi)容是:自定義了用戶認(rèn)證逞盆、角色查詢以及權(quán)限查詢的SQL語(yǔ)句
該測(cè)試用例最后的輸出結(jié)果如下:
true
false
true
true
關(guān)于shiro框架的一些基本用法介紹就到此結(jié)束了。我將在下一篇文章中介紹如何非入侵地將shiro權(quán)限管理集成到Spring等WEB開(kāi)發(fā)框架中松申,敬請(qǐng)期待云芦!