[轉(zhuǎn)] Shiro權(quán)限控制框架入門1:Shiro的認(rèn)證流程以及基本概念介紹

簡(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)期待云芦!

原文地址:https://www.zifangsky.cn/770.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末俯逾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子舅逸,更是在濱河造成了極大的恐慌桌肴,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琉历,死亡現(xiàn)場(chǎng)離奇詭異坠七,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)旗笔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門彪置,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蝇恶,你說(shuō)我怎么就攤上這事拳魁〈榛。” “怎么了想虎?”我有些...
    開(kāi)封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵舌厨,是天一觀的道長(zhǎng)忿薇。 經(jīng)常有香客問(wèn)我署浩,道長(zhǎng),這世上最難降的妖魔是什么炊汤? 我笑而不...
    開(kāi)封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任抢腐,我火速辦了婚禮迈倍,結(jié)果婚禮上捣域,老公的妹妹穿的比我還像新娘拂玻。我一直安慰自己,他們只是感情好填帽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著打肝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪争便。 梳的紋絲不亂的頭發(fā)上滞乙,一...
    開(kāi)封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天斩启,我揣著相機(jī)與錄音醉锅,去河邊找鬼。 笑死垄琐,一個(gè)胖子當(dāng)著我的面吹牛狸窘,可吹牛的內(nèi)容都是我干的坯认。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼陋气,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼恩伺!你這毒婦竟也來(lái)了晶渠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤便瑟,失蹤者是張志新(化名)和其女友劉穎到涂,沒(méi)想到半個(gè)月后颁督,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屿讽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年伐谈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诵棵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片履澳。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奇昙,死狀恐怖敌完,靈堂內(nèi)的尸體忽然破棺而出滨溉,到底是詐尸還是另有隱情长赞,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布脯颜,位于F島的核電站栋操,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏舍沙。R本人自食惡果不足惜剔宪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一葱绒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧留瞳,春花似錦骚秦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)阶剑。三九已至,卻和暖如春素邪,著一層夾襖步出監(jiān)牢的瞬間猪半,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工沽甥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留摆舟,地道東北人恨诱。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像材失,于是被迫代替她去往敵國(guó)和親龙巨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子熊响,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 身份驗(yàn)證秸弛,即在應(yīng)用中誰(shuí)能證明他就是他本人洪碳。一般提供如他們的身份ID一些標(biāo)識(shí)信息來(lái)表明他就是他本人,如提供身份證绞铃,用...
    小孩真笨閱讀 535評(píng)論 0 0
  • 一.Shiro簡(jiǎn)介 Shiro框架是和spring security框架作用差不多的一個(gè)安全認(rèn)證授權(quán)框架儿捧,但它更加...
    興厚閱讀 5,529評(píng)論 0 14
  • 一:基礎(chǔ)概念 什么是權(quán)限管理 權(quán)限管理包括用戶身份認(rèn)證和授權(quán)兩部分菲盾,簡(jiǎn)稱認(rèn)證授權(quán)各淀。對(duì)于需要訪問(wèn)控制的資源用戶首先經(jīng)...
    QGUOFENG閱讀 559評(píng)論 0 0
  • 弓長(zhǎng)是你姓 桂香八月中 芬芳乃花蕊 印在吾心中 2017.10.14. 贈(zèng)同學(xué)
    宋剛易海游龍閱讀 139評(píng)論 0 0
  • 找一種你喜歡用到工作中的語(yǔ)言修復(fù)問(wèn)題(公開(kāi))發(fā)布工作寫博客保持健康心態(tài)的小技巧 “哦溺健,天那。相比其他開(kāi)發(fā)者剖膳,我又笨...
    極客學(xué)院Wiki閱讀 1,220評(píng)論 1 6