SpringBoot使用Shiro实现权限验证

一、Shiro简介

          Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

主要功能

三个核心组件:Subject、SecurityManager 和 Realms.

  • Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
  • SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
  • Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。

  Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。

二、创建工程测试

1、简单了解Shiro的API

添加相关依赖pom.xml

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<!--shiro核心类库-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>1.2.3</version>
		</dependency>

		<!-- logback 依赖包-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>log4j-over-slf4j</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

编写shiro配置文件,在resources目录下编写shiro配置文件,shiro.ini

#用户名=密码,角色1,角色2...,角色n
[users]
root = secret, admin
guest = guest, guest
test = 123456, role1, role2
# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# 角色名=权限1,权限2...权限n
# -----------------------------------------------------------------------------
[roles]
admin = *
guest = guest
role1=perm1,perm2
role2=perm3

测试类

package com.example.demo.shiro;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;


/**
 * 路径:com.example.demo.shiro
 * 类名:
 * 功能:《用一句描述一下》
 * 备注:
 * 创建人:typ
 * 创建时间:2018/10/8 17:50
 * 修改人:
 * 修改备注:
 * 修改时间:
 */
@Slf4j
public class ShiroTest {

    public static void main(String[] args) {
        //创建 SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

        //解析配置文件,并返回一些 SecurityManager
        SecurityManager securityManager = factory.getInstance();

        //将SecurityManager
        SecurityUtils.setSecurityManager(securityManager);

        //安全操作,Subject
        Subject currentUser = SecurityUtils.getSubject();

        //测试在应用的当前会话中设置的属性
        Session session = currentUser.getSession();

        //放进去一个key
        session.setAttribute("someKey", "aValue");

        //根据key获取value
        String value = (String) session.getAttribute("someKey");
        //比较拿到的值和原来的值是否一致
        if ("aValue".equals(value)) {
            log.info("检索到正确的值[" + value + "]");
        }

        //尝试进行登录用户,如果登录失败了,我们进行一些处理
        if (!currentUser.isAuthenticated()) {
            //如果用户没有登录过
            UsernamePasswordToken token = new UsernamePasswordToken("test", "123456");
            //是否记住用户
            token.setRememberMe(true);
            try {
                currentUser.login(token);
                //当我们获登录用户之后
                log.info("用户 [" + currentUser.getPrincipal() + "] 登陆成功");
                // 查看用户是否有指定的角色
                if (currentUser.hasRole("admin")) {
                    log.info("您有admin角色");
                } else {
                    log.info("您没有admin角色");
                }
                if (currentUser.hasRole("role1")) {
                    log.info("您有role1角色");
                } else {
                    log.info("您没有role1角色");
                }

                // 查看用户是否有某个权限
                if (currentUser.isPermitted("perm1")) {
                    log.info("您有perm1权限");
                } else {
                    log.info("您没有perm1权限");
                }
                if (currentUser.isPermitted("guest")) {
                    log.info("您有guest权限");
                } else {
                    log.info("您没有guest权限");
                }

                //退出
                currentUser.logout();
            } catch (UnknownAccountException uae) {
                log.info(token.getPrincipal() + "账户不存在");
            } catch (IncorrectCredentialsException ice) {
                log.info(token.getPrincipal() + "密码不正确");
            } catch (LockedAccountException lae) {
                log.info(token.getPrincipal() + "用户被锁定了 ");
            } catch (AuthenticationException ae) {
                //无法判断是什么错了
                log.info(ae.getMessage());
            }

        }
    }

}

运行程序,查看结果如下:

21:41:25.606 [main] DEBUG org.apache.shiro.subject.support.DefaultSubjectContext - No SecurityManager available in subject context map.  Falling back to SecurityUtils.getSecurityManager() lookup.
21:41:25.610 [main] INFO com.example.demo.shiro.ShiroTest - 用户 [test] 登陆成功
21:41:25.610 [main] INFO com.example.demo.shiro.ShiroTest - 您没有admin角色
21:41:25.610 [main] INFO com.example.demo.shiro.ShiroTest - 您有role1角色
21:41:25.610 [main] INFO com.example.demo.shiro.ShiroTest - 您有perm1权限
21:41:25.634 [main] INFO com.example.demo.shiro.ShiroTest - 您没有guest权限
21:41:25.634 [main] DEBUG org.apache.shiro.mgt.DefaultSecurityManager - Logging out subject with primary principal test

2、Shiro+MySQL动态权限验证

数据库设计:

用户表(SHIRO_USER)、用户角色表(SHIRO_USER_ROLE)、角色权限表(SHIRO_ROLE_PERMISSION)

SQL如下:

# Host: 127.0.0.1  (Version 5.7.21)
# Date: 2018-10-08 22:26:32
# Generator: MySQL-Front 6.0  (Build 2.20)


#
# Structure for table "shiro_user"
#

DROP TABLE IF EXISTS `shiro_user`;
CREATE TABLE `shiro_user` (
  `id` varchar(32) DEFAULT NULL,
  `user_name` varchar(50) DEFAULT NULL,
  `password` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

#
# Data for table "shiro_user"
#

INSERT INTO `shiro_user` VALUES ('1','[email protected]','123456');


#
# Structure for table "shiro_user_role"
#

DROP TABLE IF EXISTS `shiro_user_role`;
CREATE TABLE `shiro_user_role` (
  `id` varchar(32) DEFAULT NULL,
  `role_name` varchar(50) DEFAULT NULL,
  `user_name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

#
# Data for table "shiro_user_role"
#

INSERT INTO `shiro_user_role` VALUES ('1','admin','[email protected]'),('2','test','[email protected]');

#
# Structure for table "shiro_role_permission"
#

DROP TABLE IF EXISTS `shiro_role_permission`;
CREATE TABLE `shiro_role_permission` (
  `id` varchar(32) DEFAULT NULL,
  `role_name` varchar(50) DEFAULT NULL,
  `perm_name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

#
# Data for table "shiro_role_permission"
#

INSERT INTO `shiro_role_permission` VALUES ('1','admin','perm1'),('2','test','guest');

添加数据库相关的依赖,pom.xml

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<!--shiro核心类库-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>1.2.3</version>
		</dependency>

		<!-- logback 依赖包-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>log4j-over-slf4j</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.46</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

编写shiro配置文件,在resources目录下编写shiro配置文件,shiro-mysql.ini

[main]
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://127.0.0.1:3306/mc_config
dataSource.username=root
dataSource.password=admin
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
#是否检查权限
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.dataSource=$dataSource
#重写sql语句
#根据用户名查询出密码
jdbcRealm.authenticationQuery = select PASSWORD from SHIRO_USER where USER_NAME = ?
#根据用户名查询出角色
jdbcRealm.userRolesQuery = select ROLE_NAME from SHIRO_USER_ROLE where USER_NAME = ?
#根据角色名查询出权限
jdbcRealm.permissionsQuery = select PERM_NAME from SHIRO_ROLE_PERMISSION WHERE ROLE_NAME = ?
securityManager.realms=$jdbcRealm

测试类

package com.example.demo.shiro;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;

/**
 * 路径:com.example.demo.shiro
 * 类名:
 * 功能:Shiro+MySQL动态权限验证
 * 备注:
 * 创建人:typ
 * 创建时间:2018/10/8 21:57
 * 修改人:
 * 修改备注:
 * 修改时间:
 */
@Slf4j
public class ShiroMysqlTest {

    public static void main(String[] args) {
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-mysql.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject currentUser = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("[email protected]", "123456");
        //是否记住用户
        token.setRememberMe(true);
        try {
            currentUser.login(token);
            //当我们获登录用户之后
            log.info("用户 [" + currentUser.getPrincipal() + "] 登陆成功");
            //查看用户是否有角色
            if (currentUser.hasRole("admin")) {
                log.info("您有admin角色");
            } else {
                log.info("您没有admin角色");
            }
            if (currentUser.hasRole("test")) {
                log.info("您有test角色");
            } else {
                log.info("您没有test角色");
            }
            // 查看用户是否有某个权限
            if (currentUser.isPermitted("perm1")) {
                log.info("您有perm1权限");
            } else {
                log.info("您没有perm1权限");
            }
            if (currentUser.isPermitted("guest")) {
                log.info("您有guest权限");
            } else {
                log.info("您没有guest权限");
            }
            //退出
            currentUser.logout();
        } catch (UnknownAccountException uae) {
            log.info(token.getPrincipal() + "账户不存在");
        } catch (IncorrectCredentialsException ice) {
            log.info(token.getPrincipal() + "密码不正确");
        } catch (LockedAccountException lae) {
            log.info(token.getPrincipal() + "用户被锁定了 ");
        } catch (AuthenticationException ae) {
            //无法判断是什么错了
            log.info(ae.getMessage());
        }
    }

}

运行程序,查看结果如下:

21:48:00.094 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 用户 [[email protected]] 登陆成功
21:48:00.098 [main] DEBUG org.apache.shiro.realm.AuthorizingRealm - No authorizationCache instance set.  Checking for a cacheManager...
21:48:00.098 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set.  Authorization cache cannot be obtained.
21:48:00.102 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 您有admin角色
21:48:00.102 [main] DEBUG org.apache.shiro.realm.AuthorizingRealm - No authorizationCache instance set.  Checking for a cacheManager...
21:48:00.102 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set.  Authorization cache cannot be obtained.
21:48:00.110 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 您有test角色
21:48:00.110 [main] DEBUG org.apache.shiro.realm.AuthorizingRealm - No authorizationCache instance set.  Checking for a cacheManager...
21:48:00.110 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set.  Authorization cache cannot be obtained.
21:48:00.114 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 您有perm1权限
21:48:00.114 [main] DEBUG org.apache.shiro.realm.AuthorizingRealm - No authorizationCache instance set.  Checking for a cacheManager...
21:48:00.114 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set.  Authorization cache cannot be obtained.
21:48:00.122 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 您有guest权限
21:48:00.122 [main] DEBUG org.apache.shiro.mgt.DefaultSecurityManager - Logging out subject with primary principal [email protected]

3. SpringBoot整合mybatis、shiro、redis实现基于数据库动态权限管理系统实例

。。。。。。