[轉(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í)際的開發(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開發(fā)框架中,敬請(qǐng)期待!

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 身份驗(yàn)證,即在應(yīng)用中誰(shuí)能證明他就是他本人。一般提供如他們的身份ID一些標(biāo)識(shí)信息來(lái)表明他就是他本人,如提供身份證,用...
    小孩真笨閱讀 605評(píng)論 0 0
  • 一.Shiro簡(jiǎn)介 Shiro框架是和spring security框架作用差不多的一個(gè)安全認(rèn)證授權(quán)框架,但它更加...
    興厚閱讀 5,649評(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閱讀 608評(píng)論 0 0
  • 弓長(zhǎng)是你姓 桂香八月中 芬芳乃花蕊 印在吾心中 2017.10.14. 贈(zèng)同學(xué)
    宋剛易海游龍閱讀 182評(píng)論 0 0
  • 找一種你喜歡用到工作中的語(yǔ)言修復(fù)問(wèn)題(公開)發(fā)布工作寫博客保持健康心態(tài)的小技巧 “哦,天那。相比其他開發(fā)者,我又笨...
    極客學(xué)院Wiki閱讀 1,269評(píng)論 1 6

友情鏈接更多精彩內(nèi)容