Apache Shiro最新版本1.9.1教程
配套源码Demo下载地址(建议先下载,配合教程使用,遇到问题留言):SpringBoot+ApacheShiro1.9.1最新版本详细教程,基于RBAC角色访问、安全管理框架、用户角色权限-Java文档类资源-CSDN下载
先抛出一个思考问题:
很多的用户、角色、权限时候该如何管理?
希望:成熟的安全框架又能灵活的按照自己所需进行设计,快速效率全面的完成任务。
例如:
用户登陆某管理后台后,如何判断当前用户哪些菜单可见?哪些菜单中的操作可实施呢?
不做这些验证会出现什么问题?
操作安全、数据安全、网络安全等问题
shiro 是什么?
Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API
Apache Shiro是一个功能强大且易于使用的 Java 安全框架,它执行身份验证、授权、加密和会话管理。借助 Shiro 易于理解的 API,您可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的 Web 和企业应用程序。
shiro有哪些特点?使用shiro又能够做些什么呢?
优点:快速上手、全面支持验证、授权、加密和会话、灵活自定义设计、支持web环境、可以无缝集成spring等优点。可以用来用户验证、用户授权、用户session管理、安全加密等......
特点:
全面:authentication, authorization, cryptography, and session management.
使用简单:核心的组建使用并不多,可以快速掌握。
灵活:自定义用户、角色、权限的一些体系设计
web支持:REST接口注解、url的权限字段控制config中
无缝集成:shiro starter直接集成Spring boot
能力信用背书:apache基金会
Shiro 不会去维护用户、维护权限;这些需要我们自己去设计提供(RBAC Role-Based Ac cess Control,基于角色的访问控制 );然后通过相应的接口注入给 Shiro .
shiro的架构设计(shiro重在理解流程原理并熟练使用)
shiro内部架构实现
• Subject:任何可以与应用交互的“用户/主体”;
• SecurityManager :相当于SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证、授权、会话及缓存的管理。
• Authenticator:负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
• Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
• Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要实现自己的 Realm;(授权、认证)
• SessionManager:管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web环境,也可以用在如普通的 JavaSE 环境
• CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能
• Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。
shiro基本内部架构所提供的功能
Au then ti ca tion:身份认证 / 登录,验证用户是不是拥有相应的身份; Au tho ri za tion:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限; Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的; Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储; Web Support:Web 支持,可以非常容易的集成到 Web 环境; Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率; Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去; Testing:提供测试支持; Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问; Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了
shiro有哪些特点?使用shiro又能够做些什么呢?
优点:快速上手、全面支持验证、授权、加密和会话、灵活自定义设计、支持web环境、可以无缝集成spring等优点。可以用来用户验证、用户授权、用户session管理、安全加密等......
Shiro 不会去维护用户、维护权限;这些需要我们自己去设计提供(RBAC Role-Based Ac cess Control,基于角色的访问控制 );然后通过相应的接口注入给 Shiro .
shiro的架构核心组成都有哪些东西?
重点原理流程
·
Subject:
主体对象,对其所有交互都会委托给SecurityManager。例如:张三
SecurityManager:
安全管理器,与安全有关的操作进行操作处理,SecurityManager 要验证用户身份,需要从 Realm 获取比较确定。例如:张三是否登陆有效
Realm:
安全域(安全数据信息,如用户、角色、权限)。例如:这里存储着张三的安全数据信息
如何使shiro
技术选型(全部最新)
springboot
shiro
mybatis
mybatis plus
mysql
thymeleaf
实现功能:
登陆认证、
密码加密、
权限授权、
基于角色访问控制RBAC建表sql:
用户表 tb_user
角色表tb_role
权限表tb_permission
用户角色表tb_user_role
角色权限tb_role_permission
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_permission
-- ----------------------------
DROP TABLE IF EXISTS `tb_permission`;
CREATE TABLE `tb_permission` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '权限编号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='权限表';
-- ----------------------------
-- Records of tb_permission
-- ----------------------------
BEGIN;
INSERT INTO `tb_permission` VALUES (1, 'user:add');
INSERT INTO `tb_permission` VALUES (2, 'user:update');
INSERT INTO `tb_permission` VALUES (3, 'user:query');
COMMIT;
-- ----------------------------
-- Table structure for tb_role
-- ----------------------------
DROP TABLE IF EXISTS `tb_role`;
CREATE TABLE `tb_role` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '角色名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='角色表';
-- ----------------------------
-- Records of tb_role
-- ----------------------------
BEGIN;
INSERT INTO `tb_role` VALUES (1, 'admin');
INSERT INTO `tb_role` VALUES (2, 'user');
INSERT INTO `tb_role` VALUES (3, 'superadmin');
COMMIT;
-- ----------------------------
-- Table structure for tb_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `tb_role_permission`;
CREATE TABLE `tb_role_permission` (
`id` bigint NOT NULL AUTO_INCREMENT,
`role_id` bigint DEFAULT NULL COMMENT '角色编号',
`permission_id` bigint DEFAULT NULL COMMENT '权限编号',
`permission_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='角色权限表';
-- ----------------------------
-- Records of tb_role_permission
-- ----------------------------
BEGIN;
INSERT INTO `tb_role_permission` VALUES (4, 1, 1, 'user:add');
INSERT INTO `tb_role_permission` VALUES (5, 3, 2, 'user:update');
INSERT INTO `tb_role_permission` VALUES (6, 1, 3, 'user:query');
INSERT INTO `tb_role_permission` VALUES (7, 3, 1, 'user:add');
INSERT INTO `tb_role_permission` VALUES (8, 3, 3, 'user:query');
COMMIT;
-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL COMMENT '用户名',
`password` varchar(100) DEFAULT NULL COMMENT '密码',
`salt` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1547863866599714819 DEFAULT CHARSET=utf8 COMMENT='用户表\n';
-- ----------------------------
-- Records of tb_user
-- ----------------------------
BEGIN;
INSERT INTO `tb_user` VALUES (1547617365042642946, 'zhangquancheng', '120f857f67a88a65c88aa67a7ba1923e', 'vBtmuh5Utid6okp5jOuiuA==');
INSERT INTO `tb_user` VALUES (1547863866599714818, 'superadmin', 'a938338a6e1d2f23afe233351a1c0dc5', 'pCVOU0u0TEtJ3uqNm4IDvw==');
COMMIT;
-- ----------------------------
-- Table structure for tb_user_role
-- ----------------------------
DROP TABLE IF EXISTS `tb_user_role`;
CREATE TABLE `tb_user_role` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint DEFAULT NULL COMMENT '用户编号',
`role_id` bigint DEFAULT NULL COMMENT '角色编号',
`role_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='用户角色表';
-- ----------------------------
-- Records of tb_user_role
-- ----------------------------
BEGIN;
INSERT INTO `tb_user_role` VALUES (4, 1547617365042642946, 1, 'admin');
INSERT INTO `tb_user_role` VALUES (5, 1547863866599714818, 3, 'superadmin');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
Maven依赖
项目配置文件
server:
port: 8801
spring:
datasource:
username: root
password: rootroot
url: jdbc:mysql://localhost:3306/shiro?useSSL=true&useUnicode=true&characterEncoding=UTF-8
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
type-aliases-package: com.zhangquancheng.shirodemo.entity
mapper-locations: classpath:mapper/*.xml
表对象类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TbUser {
private Long id;
private String username;
private String password;
private String salt;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TbRole {
private Long id;
private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TbPermission {
private Long id;
private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TbUserRole {
private Long id;
private Long userId;
private Long roleId;
private String roleName;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TbRolePermission {
private Long id;
private Long roleId;
private Long permissionId;
private String permissionName;
}
controlle方法入口类
@Controller
public class LoginController {
/**
* @Description 登陆页面
*/
@RequestMapping({"/toLogin","/"})
public String toLogin(Model model){
return "login";
}
}
登陆页面login.html
登陆
项目启动测试
@SpringBootApplication
//@MapperScan("com.zhangquancheng.shirodemo.mapper")
public class ShiroDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ShiroDemoApplication.class, args);
}
}
mapper类、service/impl类、XML 自动生成(略) shiroConfig配置类
@Configuration
@Lazy
public class ShiroConfig {
//ShiroFilterFactoryBean
//DefaultWebSecurityManager
//UserRealm
// /**
// * 异常映射解析器
// *
// * SimpleMappingExceptionResolver 启动是在tomcat启动的时候执行
// * MyExceptionHandler 每次url访问的时候启动
// */
// @Bean
// public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
// SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
// Properties properties = new Properties();
// /*未授权处理页*/
// properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/403");
// /*⾝份没有验证*/
// properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "/toLogin");
// resolver.setExceptionMappings(properties);
// return resolver;
// }
// /**
// * Session Manager:会话管理
// * 即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;
// * 会话可以是普通JavaSE环境的,也可以是如Web环境的;
// * 管理所有Subject的session包括创建、维护、删除、失效、验证等工作
// *
// * DefaultSessionManager 用于JavaSE环境
// * ServletContainerSessionManager 用于Web环境,直接使用servlet容器的会话。
// * DefaultWebSessionManager 用于web环境自己维护会话
// */
// @Bean("sessionManager")
// public SessionManager sessionManager(){
// DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
// sessionManager.
// //设置session过期时间
// sessionManager.setGlobalSessionTimeout(60 * 60 * 1000);
// sessionManager.setSessionValidationSchedulerEnabled(true);
// // 去掉shiro登录时url里的JSESSIONID
// sessionManager.setSessionIdUrlRewritingEnabled(false);
// return sessionManager;
// }
/**
* ShiroFilter是整个Shiro的入口点,用于拦截需要安全控制的请求进行处理
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilter.setSecurityManager(securityManager);
//登陆请求
shiroFilter.setLoginUrl("/toLogin");
/**
* 拦截设置
* anon:无需认证就可以访问
* authc:必须认证了才可以访问
* user:必须拥有记住我功能才能用
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
* */
Map
//当前请求地址必须认证后访问
filterMap.put("/user/login","anon");
filterMap.put("/user/*","authc");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
/**
* SecurityManager:安全管理器
*/
@Bean(name = "securityManager")
// public DefaultWebSecurityManager securityManager(@Qualifier("sessionManager") SessionManager sessionManager,@Qualifier("userRealm") UserRealm userRealm) {
public DefaultWebSecurityManager securityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置realm
securityManager.setRealm(userRealm);
//设置会话管理sessionManager
// securityManager.setSessionManager(sessionManager);
return securityManager;
}
//第一步:创建realm对象,并被Spring托管
@Bean(name = "userRealm")
public UserRealm userRealm() {
UserRealm userRealm = new UserRealm();
//指定处理加解密的匹配工具类
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
hashedCredentialsMatcher.setHashIterations(1);
userRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return userRealm;
}
//-------------------------------------开启shiro注解
/**
* 管理Shiro中一些bean的生命周期
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 扫描上下文,寻找所有的Advistor(通知器)
* 将这些Advisor应用到所有符合切入点的Bean中。
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
/**
* 匹配所有加了 Shiro 认证注解的方法
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
/**
* 配置ShiroDialect:用于thymeleaf和shiro标签配合使用
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
realm配置类
public class UserRealm extends AuthorizingRealm {
@Autowired
TbUserRoleMapper tbUserRoleMapper;
@Autowired
TbRolePermissionMapper tbRolePermissionMapper;
@Autowired
TbUserMapper tbUserMapper;
/**
* @Description 授权 权限匹配
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("AuthorizationInfo -----------授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登录的这个对象
Subject subject = SecurityUtils.getSubject();
TbUser currentUsers = (TbUser) subject.getPrincipal();
//获取角色
QueryWrapper queryWrapper = new QueryWrapper
queryWrapper.select().eq("user_id",currentUsers.getId());
List
//获取权限
queryWrapper = new QueryWrapper
queryWrapper.select().in("role_id",tbUserRoles.stream().map(TbUserRole::getRoleId).collect(Collectors.toList()));
List
//设置角色与权限
info.setRoles(tbUserRoles.stream().map(i->i.getRoleName()).collect(Collectors.toSet()));
info.setStringPermissions(tbRolePermissions.stream().map(i->i.getPermissionName()).collect(Collectors.toSet()));
return info;
}
/**
* @Description 认证 账号密码匹配(登陆)
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("AuthorizationInfo -----------认证");
//用户名,密码,从数据库中取
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
QueryWrapper queryWrapper = new QueryWrapper
queryWrapper.select("id","password","username","salt").eq("username",userToken.getUsername());
TbUser dbuser = tbUserMapper.selectOne(queryWrapper);
//不存在这个用户
if (Objects.isNull(dbuser)) {
//throw new UnknownAccountException("不存在用户");
return null;
}
//密码认证,shiro去做
return new SimpleAuthenticationInfo(dbuser, dbuser.getPassword(), ByteSource.Util.bytes(dbuser.getSalt()),this.getName());
}
}
异常捕获跳转页面。
@ControllerAdvice//controller增强器,捕获controller异常
public class MyExcetionHandler {
@ExceptionHandler(UnauthorizedException.class)
public String handlerUnauthorizedException(UnauthorizedException exception){
System.err.println("handlerUnauthorizedException");
exception.printStackTrace();
return "/403";
}
// @RequestBody
@ExceptionHandler(UnauthenticatedException.class)
public String unauthenticatedException(UnauthenticatedException exception){
System.err.println("UnauthenticatedException");
exception.printStackTrace();
return "/toLogin";
}
}
shiro异常种类 && shiro thymeleaf的常见标签:
shiro的常见异常
1.1 AuthenticationException 异常是Shiro在登录认证过程中,认证失败需要抛出的异常。 AuthenticationException包含以下⼦类:
1.1.1 CredentitalsException 凭证异常IncorrectCredentialsException 不正确的凭证ExpiredCredentialsException 凭证过期
1.1.2 AccountException 账号异常
ConcurrentAccessException 并发访问异常(多个⽤户同时登录时抛出)
UnknownAccountException 未知的账号
ExcessiveAttemptsException 认证次数超过限制DisabledAccountException 禁⽤的账号
LockedAccountException 账号被锁定
pportedTokenException 使⽤了不⽀持的Token
1.2AuthorizationException: 包含⼦类:
1.2.1 UnauthorizedException:抛出以指⽰请求的操作或对请求的资源的访问是不允许的。
1.2.2 UnanthenticatedException:当尚未完成成功认证时,尝试执⾏授权操作时引发异常。
登陆后的首页
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
开放页面(仅让超级管理员进入)
无权限页面
对不起无权限
操作成功页面
相关文档介绍
官方文档:10分钟快速开始
中文资料:w3c介绍
发表评论