【Shiro从入门到精通】——一篇文章让小白也能快速了解并掌握基础Shiro知

🎼个人主页:【Y小夜】

😎作者简介:一位双非学校的大二学生,编程爱好者,

专注于基础和实战分享,欢迎私信咨询!

🎆入门专栏:🎇【MySQLJava基础Rust

🎈热门专栏:🎊【PythonJavawebVue框架

感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持!❤️

目录

🎯什么是 Apache Shiro?

🎯它拥有哪些功能

🎯外部框架

🎯内部架构

🎯快速开始

🎯SpringBoot集成Shiro

😎准备工作

😎整合Shiro

😎页面拦截实现

🎯登录认证操作

🎯整合数据库

🎯Shiro授权

🎯整合Thymeleaf


🎯什么是 Apache Shiro?

        Apache Shiro 是一个功能强大且灵活的开源安全框架,可以干净地处理身份验证,授权,企业会话 Management 和加密。

        Apache Shiro 的首要目标是易于使用和理解。安全有时可能非常复杂,甚至会很痛苦,但不一定如此。框架应尽可能掩盖复杂性,并公开干净直观的 API,以简化开发人员确保其应用程序安全的工作。

您可以使用 Apache Shiro 进行以下操作:

  • 验证用户身份以验证其身份

  • 对用户执行访问控制,例如:

                确定是否为用户分配了特定的安全角色

                确定是否允许用户做某事

  • 即使在没有 Web 或 EJB 容器的情况下,也可以在任何环境中使用 Session API。

  • 在身份验证,访问控制或会话的生存期内对事件做出反应。

  • 汇总 1 个或更多用户安全数据的数据源,并将其全部显示为单个复合用户“视图”。

  • 启用单点登录(SSO)功能

  • 启用“记住我”服务以进行用户关联而无需登录

    以及更多-所有这些都集成到一个易于使用的内聚 API 中。

        Shiro 尝试在所有应用程序环境中实现这些目标-从最简单的命令行应用程序到最大的企业应用程序,而不必强加对其他第三方框架,容器或应用程序服务器的依赖。当然,该项目旨在尽可能地集成到这些环境中,但是可以在任何环境中直接使用它。        

🎯它拥有哪些功能

Apache Shiro 是具有许多功能的全面的应用程序安全框架。下图显示了 Shiro 集中精力的地方

  • Authentication:身份认证、登录,验证用户是不是拥有相应的身份;
  • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行什么操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限!
  • Session Manager:会话管理,即用户登录后就是第一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通的JavaSE环境,也可以是Web环境;
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;
  • Web SupportWeb支持,可以非常容易的集成到Web环境;
  • Caching:缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高效率
  • ConcurrencyShiro支持多线程应用的并发验证,即,如在一个线程中开启另一个线程,能把权限自动的传播过去
  • Testing:提供测试支持;
  • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
  • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

🎯外部框架

        在最高概念级别,Shiro 的体系结构具有 3 个主要概念:SubjectSecurityManagerRealms。下图是这些组件如何交互的高级概述,我们将在下面介绍每个概念:

  •         SecurityManager:安全管理器,即所有与安全有关的操作都会与SercurityManager交互,并且它 管理着所有的Subject,可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色
  •         subject: 应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject,Subject代表了当前的用户,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等,与Subject的所有交互都会委托给SecurityManagerSubject其实是一个门面,SecurityManageer 才是实际的执行者
  •         Realm:ShiroRealm获取安全数据(如用户,角色,权限),就是说SecurityManager 要验证用户身份,那么它需要从Realm 获取相应的用户进行比较,来确定用户的身份是否合法;也需要从Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行,可以把Realm看成DataSource;

🎯内部架构

下图显示了 Shiro 的核心体系结构概念,并简要概述了每个概念:

  • SecurityManager是 Shiro 架构的核心。它主要是一个“伞”对象,用于协调其托管组件以确保它们能够顺利协同工作。它还 ManagementShiro 对每个应用程序用户的视图,因此它知道如何对每个用户执行安全性操作。

  • Authenticator是负责执行用户的身份验证(登录)并对其作出反应的组件。当用户尝试登录时,该逻辑由Authenticator执行。 Authenticator知道如何与一个或多个存储相关用户/帐户信息的Realms进行协调。从这些Realms获得的数据用于验证用户的身份,以确保用户确实是他们所说的真实身份。

  • 如果配置了多个Realm,则AuthenticationStrategy将协调领域以确定身份验证尝试成功或失败的条件(例如,如果一个领域成功但其他领域失败,则该尝试成功吗?是否所有领域都必须成功?第一?)。

  • Authorizer是负责确定应用程序中用户访问控制的组件。它是最终表明是否允许用户做某事的机制。与Authenticator一样,Authorizer也知道如何与多个后端数据源进行协调以访问角色和权限信息。 Authorizer使用此信息来确定是否允许用户执行给定的动作。

  • SessionManager知道如何创建和 Management 用户Session的生命周期,以便为所有环境中的用户提供可靠的会话体验。这是安全框架领域中的一项独特功能-Shiro 能够在任何环境中本地 Management 用户会话,即使没有 Web/Servlet 或 EJB 容器也可以。默认情况下,Shiro 将使用现有的会话机制(例如 Servlet 容器)(如果可用),但是如果没有这种机制(例如在独立应用程序或非 Web 环境中),它将使用其内置的企业会话 Management 来提供相同的编程经验。 SessionDAO的存在是为了允许使用任何数据源来保留会话。

  • SessionDAO代表SessionManager执行Session持久性(CRUD)操作。这允许将任何数据存储插入会话 Management 基础结构。

  • CacheManager创建和 Management 其他 Shiro 组件使用的Cache实例生命周期。由于 Shiro 可以访问许多后端数据源以进行身份验证,授权和会话 Management,因此缓存一直是框架中的一流架构功能,可以在使用这些数据源时提高性能。可以将任何现代的开源和/或企业缓存产品插入 Shiro,以提供快速有效的用户体验。

  • Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于密码加密,解密等
  • Realm:可以有一个或者多个的realm,可以认为是安全实体数据源,即用于获取安全实体的,可以用JDBC实现,也可以是内存实现等等,由用户提供;所以一般在应用中都需要实现自己的realm

🎯快速开始

1.创建一个 maven 父工程,用于学习 Shiro
2.导入依赖
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<!-- Shiro uses SLF4J for logging. We'll use the 'simple' binding
in this example app. See http://www.slf4j.org for more info. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
<scope>test</scope>
</dependency>
</dependencies>

        我们将使用 INI 文件为该简单应用程序配置 Shiro SecurityManager。首先,从pom.xml所在的目录创建一个 src/main/resources 目录。然后在新目录中创建一个shiro.ini文件,其内容如下:

# =============================================================================
# Tutorial INI configuration
#
# Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
# =============================================================================

# -----------------------------------------------------------------------------
# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz

# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# -----------------------------------------------------------------------------
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5

        此配置基本上设置了一小组静态用户帐户,足以满足我们的第一个应用程序的需要。在后面的章节中,您将看到我们如何使用关系数据库,LDAP 和 ActiveDirectory 等更复杂的 User 数据源。

        现在已经定义了一个 INI 文件,我们可以在 Tutorial 应用程序类中创建SecurityManager实例。更改main方法以反映以下更新:

public static void main(String[] args) {

    log.info("My First Apache Shiro Application");

    //1.
    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

    //2.
    SecurityManager securityManager = factory.getInstance();

    //3.
    SecurityUtils.setSecurityManager(securityManager);

    System.exit(0);
}

        随意运行mvn compile exec:java并查看一切仍可成功运行(由于 Shiro 的默认调试日志记录或更低版本,您将看不到任何 Shiro 日志消息-如果它启动并运行无错误,则说明一切仍然正常)。

        现在我们的 SecurityManager 已经设置好并且可以使用了,现在我们可以开始做我们 true 关心的事情了-执行安全操作。

🎯SpringBoot集成Shiro

😎准备工作

导入 Maven依赖thymeleaf
<!--thymeleaf模板-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
编写一个页面 index.html templates
<!DOCTYPE html>
<html lang="en"xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
</body>
</html>
编写 controller 进行访问测试
package com.kuang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping({"/","/index"})
public String toIndex(Model model){
model.addAttribute("msg","hello,Shiro");
return "index";
}
}
thymeleaf

😎整合Shiro

导入Shiro spring整合的依赖

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency
编写 Shiro 配置类 config包
package com.kuang.config;
import org.springframework.context.annotation.Configuration;
//声明为配置类
@Configuration
public class ShiroConfig {
//创建 ShiroFilterFactoryBean
//创建 DefaultWebSecurityManager
//创建 realm 对象
}
创建一个 realm 对象
package com.kuang.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
//自定义Realm
public class UserRealm extends AuthorizingRealm {
//执行授权逻辑
@Override
protected AuthorizationInfo
doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了=>授权逻辑PrincipalCollection");
return null;
}
//执行认证逻辑
@Override
protected AuthenticationInfo
doGetAuthenticationInfo(AuthenticationToken token) throws
AuthenticationException {
System.out.println("执行了=>认证逻辑AuthenticationToken");
return null;
    }
}
将这个类注册到我们的Bean中! ShiroConfig
@Configuration
public class ShiroConfig {
//创建 ShiroFilterFactoryBean
//创建 DefaultWebSecurityManager
//创建 realm 对象
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
接下来我们该去创建 DefaultWebSecurityManager 了
//创建 DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager
getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new
DefaultWebSecurityManager();
//关联Realm
securityManager.setRealm(userRealm);
return securityManager;
}
接下来我们该去创建 ShiroFilterFactoryBean 了
//创建 ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean
getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurity
Manager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new
ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
最后上完整的配置:
package com.kuang.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//声明为配置类
@Configuration
public class ShiroConfig {
//创建 ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean
getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityMan
ager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new
ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
//创建 DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager
getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new
DefaultWebSecurityManager();
//关联Realm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建 realm 对象
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}

😎页面拦截实现

跳转到页面的 controller
@RequestMapping("/user/add")
public String toAdd(){
return "user/add";
}
@RequestMapping("/user/update")
public String toUpdate(){
return "user/update";
}

添加Shiro的内置过滤器

@Bean
public ShiroFilterFactoryBean
getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurit
yManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new
ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
/*
添加Shiro内置过滤器,常用的有如下过滤器:
anon: 无需认证就可以访问
authc: 必须认证才可以访问
user: 如果使用了记住我功能就可以直接访问
perms: 拥有某个资源权限才可以访问
role: 拥有某个角色权限才可以访问
*/
Map<String,String> filterMap = new LinkedHashMap<String, String>();
filterMap.put("/user/add","authc");
filterMap.put("/user/update","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}

🎯登录认证操作

编写一个登录的 controller
//登录操作
@RequestMapping("/login")
public String login(String username,String password,Model model){
//使用shiro,编写认证操作
//1. 获取Subject
Subject subject = SecurityUtils.getSubject();
//2. 封装用户的数据
UsernamePasswordToken token = new UsernamePasswordToken(username,
password);
//3. 执行登录的方法,只要没有异常就代表登录成功!
try {
subject.login(token); //登录成功!返回首页
return "index";
} catch (UnknownAccountException e) { //用户名不存在
model.addAttribute("msg","用户名不存在");
return "login";
} catch (IncorrectCredentialsException e) { //密码错误
model.addAttribute("msg","密码错误");
return "login";
}
}
UserRealm 中编写用户认证的判断逻辑
//执行认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken
token) throws AuthenticationException {
System.out.println("执行了=>认证逻辑AuthenticationToken");
//假设数据库的用户名和密码
String name = "root";
String password = "123456";
//1.判断用户名
UsernamePasswordToken userToken = (UsernamePasswordToken)token;
if (!userToken.getUsername().equals(name)){
//用户名不存在
return null; //shiro底层就会抛出 UnknownAccountException
}
//2. 验证密码,我们可以使用一个AuthenticationInfo实现类
SimpleAuthenticationInfo
// shiro会自动帮我们验证!重点是第二个参数就是要验证的密码!
return new SimpleAuthenticationInfo("", password, "");
}

🎯整合数据库

导入 Mybatis 相关依赖
<!-- 引入 myBatis,这是 MyBatis官方提供的适配 Spring Boot 的,而不是Spring
Boot自己的-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
改造 UserRealm ,连接到数据库进行真实的操作!
//自定义Realm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//执行授权逻辑
@Override
protected AuthorizationInfo
doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了=>授权逻辑PrincipalCollection");
return null;
}
//执行认证逻辑
@Override
protected AuthenticationInfo
doGetAuthenticationInfo(AuthenticationToken token) throws
AuthenticationException {
System.out.println("执行了=>认证逻辑AuthenticationToken");
UsernamePasswordToken userToken = (UsernamePasswordToken)token;
//真实连接数据库
User user =
userService.queryUserByName(userToken.getUsername());
if (user==null){
//用户名不存在
return null; //shiro底层就会抛出 UnknownAccountException
}
return new SimpleAuthenticationInfo("", user.getPwd(), "");
}
}

🎯Shiro授权

UserRealm 中添加授权的逻辑,增加授权的字符串!
//执行授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection
principals) {
System.out.println("执行了=>授权逻辑PrincipalCollection");
//给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加资源的授权字符串
info.addStringPermission("user:add");
return info;
}
我们再次登录测试,发现登录的用户是可以进行访问 add 页面了!授权成功!

🎯整合Thymeleaf

添加 Maven 的依赖;
<!--
https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf
-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency
配置一个 shiro Dialect ,在 shiro 的配置中增加一个 Bean
//配置ShiroDialect:方言,用于 thymeleaf 和 shiro 标签配合使用
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
        为了完美,我们在用户登录后应该把信息放到Session 中,我们完善下!在执行认证逻辑时候,加 入session
Subject subject = SecurityUtils.getSubject();
subject.getSession().setAttribute("loginUser",user);

评论 92
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y小夜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值