Shiro安全框架
1 认识Shiro
1.1 Shiro介绍
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
-
Apache Shiro 是 Java 的一个安全( 权限)框架。
-
Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在 JavaEE 环境。
-
Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、 缓存等。
-
下载: http://shiro.apache.org/
1.2 主要功能
三个核心组件:Subject, SecurityManager 和 Realms。
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
-
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实现。
核心的类及其作用:
- Authentication: 身份认证/登录,验证用户是不是拥有相应的身份;
- Authorization: 授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用
户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户
对某个资源是否具有某个权限; - Session Manager: 会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有
信息都在会话中; 会话可以是普通 JavaSE 环境, 也可以是 Web 环境的; - Cryptography: 加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
- Web Support: Web 支持,可以非常容易的集成到Web 环境;
- Caching: 缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
- Concurrency: Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
- Testing:提供测试支持;
- Run As: 允许一个用户假装为另一个用户(如果他们允许) 的身份进行访问;
- Remember Me: 记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登
录了
2 SpringBoot集成Shiro
2.1 创建Shiro环境
创建一个普通SpringBoot的Web工程01-Shiro添加依赖包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
2.2 配置Shiro
2.2.1定义类com.feixun.shiro.config.ShiroConfig类
package com.feixun.shiro.config;
import com.feixun.shiro.realm.MyRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author feixun
* @create 2021-01-07 14:17
*/
// 标记当前类是一个Spring的配置类,用于模拟Spring的配置文件
@Configuration
public class ShiroConfig {
/**
* 配置一个SecurityManager 安全管理器
* @param myRealm
* @return
*/
@Bean
public SecurityManager securityManager(Realm myRealm){
// 选择这个默认的子类
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//设置一个Realm,这个Realm是最终用于完成我们的认证号和授权操作的具体对象
defaultWebSecurityManager.setRealm(myRealm);
return defaultWebSecurityManager;
}
/**
* 配置一个自定义的Realm的bean,最终将使用这个bean返回的对象来完成我们的认证和授权
* @return
*/
@Bean
public MyRealm myRealm(){
MyRealm myRealm = new MyRealm();
return myRealm;
}
//配置一个Shiro的过滤器bean,这个bean将配置Shiro相关的一个规则的拦截
//例如什么样的请求可以访问什么样的请求不可以访问等等
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
// 创建Shiro的拦截的拦截器 ,用于拦截我们的用户请求
ShiroFilterFactoryBean shiroFilter=new ShiroFilterFactoryBean();
// 设置Shiro的安全管理,设置管理的同时也会指定某个Realm 用来完成我们权限分配
shiroFilter.setSecurityManager(securityManager);
//用于设置一个登录的请求地址,这个地址可以是一个html或jsp的访问路径,也可以是一个控制器的路径
//作用是用于通知Shiro我们可以使用这里路径转向到登录页面,但Shiro判断到我们当前的用户没有登录时就会自动转换到这个路径
//要求用户完成成功
shiroFilter.setLoginUrl("/");
//登录成功后转向页面,由于用户的登录后期需要交给Shiro完成,因此就需要通知Shiro登录成功之后返回到那个位置
shiroFilter.setSuccessUrl("/success");
//用于指定没有权限的页面,当用户访问某个功能是如果Shiro判断这个用户没有对应的操作权限,那么Shiro就会将请求
//转向到这个位置,用于提示用户没有操作权限
shiroFilter.setUnauthorizedUrl("/noPermission");
//定义一个Map集合,这个Map集合中存放的数据全部都是规则,用于设置通知Shiro什么样的请求可以访问什么样的请求不可以访问
Map<String,String> filterChainMap=new LinkedHashMap<String,String>();
// /login 表示某个请求的名字 anon 表示可以使用游客什么进行登录(这个请求不需要登录)
filterChainMap.put("/login","anon");
//我们可以在这里配置所有的权限规则这列数据真正是需要从数据库中读取出来
//或者在控制器中添加Shiro的注解
// /admin/** 表示一个请求名字的通配, 以admin开头的任意子孙路径下的所有请求
// authc 表示这个请求需要进行认证(登录),只有认证(登录)通过才能访问
// 注意: ** 表示任意子孙路径
// * 表示任意的一个路径
// ? 表示 任意的一个字符
filterChainMap.put("/admin/**","authc");
filterChainMap.put("/user/**","authc");
//表示所有的请求路径全部都需要被拦截登录,这个必须必须写在Map集合的最后面,这个选项是可选的
//如果没有指定/** 那么如果某个请求不符合上面的拦截规则Shiro将放行这个请求
//filterChainMap.put("/**","authc");
// 设置权限拦截规则
shiroFilter.setFilterChainDefinitionMap(filterChainMap);
return shiroFilter;
}
}
2.2.2定义com.feixun.shiro.realm.MyRealm类
package com.feixun.shiro.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.Realm;
/**
* @author feixun
* @create 2021-01-20 16:45
*/
// 自定义Realm永远完成具体的认证和授权操作
// Realm的父类抽象类
// AuthenticatingRealm 只负责认证(登录)的Realm父类
// AuthorizingRealm 负责认证(登录)和授权 的Realm父类
public class MyRealm implements Realm {
@Override
public String getName() {
return null;
}
@Override
public boolean supports(AuthenticationToken authenticationToken) {
return false;
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
2.2.3 定义 com.feixun.shiro.controller.UserController类
@Controller
public class UserController {
@RequestMapping("/")
public String index(){
return "login";
}
@RequestMapping("/login")
public String login(String username, String password, Model model){
return "redirect:/success";
}
@RequestMapping("/success")
public String loginSuccess(){
return "success";
}
@RequestMapping("/noPermission")
public String noPermission(){
return "noPermission";
}
@RequestMapping("/admin/test")
@ResponseBody
public String adminTest(){
return "Admin Test 请求";
}
@RequestMapping("/user/test")
@ResponseBody
public String userTest(){
return "User Test 请求";
}
@RequestMapping("/admin/add")
@ResponseBody
public String adminAdd(){
Subject subject= SecurityUtils.getSubject();
return "这是Admin Add请求";
}
}
2.2.4 定义login.html 、nopermission.html、success.html
- 页面:login.html
<form action