一、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实现基于数据库动态权限管理系统实例
。。。。。。