当前位置:首页 > 技术知识 > 正文内容

深入探讨 Spring Boot3 整合 Apache Shiro 如何对未登录请求的拦截

maynowei6个月前 (09-13)技术知识54

在当今互联网软件开发的大环境下,保障应用程序的安全性是至关重要的一环。对于众多使用 Spring Boot3 框架进行开发的项目而言,整合强大的安全框架 Apache Shiro 可以有效提升系统的安全性。其中,实现对未登录请求的拦截是保障系统资源不被非法访问的关键措施。本文将详细地为各位互联网软件开发人员讲解在 Spring Boot3 中整合 Apache Shiro 来拦截未登录请求的方法及相关原理。

Shiro 安全框架简介

Apache Shiro 是一个强大且易用的 Java 安全框架,它提供了认证、授权、加密和会话管理等功能。在应用程序中,Shiro 主要通过以下三个核心组件协同工作来实现安全控制:

Subject:代表了当前用户的安全操作,它是应用程序与 Shiro 交互的主要接口。通过 Subject,应用代码可以进行认证和授权等操作。例如,当用户尝试登录时,我们通过 Subject 的 login 方法来执行登录操作。

SecurityManager:它是 Shiro 框架的核心,采用典型的 Facade 模式。Shiro 通过 SecurityManager 来管理内部组件实例,并提供安全管理的各种服务。在认证过程中,Subject 会委托给 SecurityManager 进行实际的认证逻辑处理。

Realm:Realm 充当了 Shiro 与应用安全数据间的 “桥梁” 或者 “连接器”。当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。例如,我们可以在 Realm 中查询数据库,获取用户的正确密码用于登录校验,以及获取用户所拥有的角色和权限信息用于授权校验。

Spring Boot3 与 Shiro 整合的准备工作

(一)添加依赖

在 Spring Boot3 项目中,我们首先需要在pom.xml文件中引入 Shiro 相关的依赖。如下是引入 Shiro Spring Boot Starter 的示例代码:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.12.0</version>
</dependency>

同时,根据项目的实际需求,可能还需要引入其他相关依赖,比如数据库驱动(如果需要从数据库获取用户信息和权限信息)、Spring Web 依赖等。

(二)创建 Shiro 配置类

接下来,我们需要创建一个 Shiro 的配置类,用于配置 Shiro 的核心组件和相关规则。在 Spring Boot 中,配置类通常使用@Configuration注解标识。以下是一个基本的 Shiro 配置类示例:

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;

@Configuration
public class ShiroConfig {

    // 配置核心安全事务管理器
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager() {
        return new DefaultWebSecurityManager();
    }

    // 配置Shiro过滤器链
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
        factory.setSecurityManager(securityManager);

        // 设置登录页和未授权页
        factory.setLoginUrl("/login");
        factory.setUnauthorizedUrl("/403");

        // 定义过滤规则
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/static/**", "anon"); // 匿名访问静态资源
        filterMap.put("/login", "anon"); // 登录页无需认证
        filterMap.put("/admin/**", "roles(admin)"); // 需要admin角色才能访问
        filterMap.put("/**", "authc"); // 其他路径需要认证

        factory.setFilterChainDefinitionMap(filterMap);
        return factory;
    }
}

在上述配置类中,我们主要完成了以下几个关键配置:

创建SecurityManager:通过@Bean注解创建了一个DefaultWebSecurityManager实例,它将作为 Shiro 的安全管理器,负责管理整个应用的安全相关操作。

配置ShiroFilterFactoryBean:这是 Shiro 与 Web 应用集成的关键组件,它负责创建 Shiro 的过滤器链,拦截请求并执行相应的安全规则。

  • 设置安全管理器:通过setSecurityManager方法将前面创建的DefaultWebSecurityManager实例设置给ShiroFilterFactoryBean。
  • 设置登录页和未授权页:使用setLoginUrl方法设置用户未登录时跳转到的登录页面路径,这里设置为/login;使用setUnauthorizedUrl方法设置用户没有权限访问资源时跳转到的未授权页面路径,这里设置为/403。
  • 定义过滤规则:通过FilterChainDefinitionMap来定义请求路径与过滤器之间的映射关系。例如,filterMap.put("/static/**", "anon")表示对/static/目录下的所有资源采用anon过滤器,即匿名用户可以直接访问,无需登录认证;filterMap.put("/admin/**", "roles(admin)")表示访问/admin/目录下的资源需要用户拥有admin角色;filterMap.put("/**", "authc")表示其他所有路径都需要用户进行认证才能访问。

Shiro 的拦截器详解

(一)常用拦截器介绍

Shiro 提供了一系列的拦截器,用于对请求进行不同类型的处理。以下是一些常用的拦截器及其功能说明:

  1. anon:匿名拦截器,不需要登录即可访问的资源,常用于过滤静态资源,如图片、CSS、JavaScript 文件等。例如,配置/static/** = anon,则所有位于/static/目录下的资源都可以被匿名用户直接访问。
  2. authc:登录拦截器,需要用户进行认证登录才能访问的资源。当配置了/user/* = authc时,意味着访问/user/目录下的任何资源,用户都必须先登录。
  3. perms:权限授权拦截器,用于验证用户是否拥有特定的权限。比如filterMap.put("/user/update", "perms(user:update)"),表示用户必须拥有user:update权限才能访问/user/update路径。
  4. roles:角色授权拦截器,验证用户是否拥有指定的角色。例如filterMap.put("/admin/delete", "roles(admin)"),只有拥有admin角色的用户才能访问/admin/delete路径。
  5. logout:登出拦截器,主要用于处理用户的登出操作。可以通过设置redirectURL属性来指定用户登出后重定向的地址。

(二)拦截器优先级

在配置多个拦截器时,了解它们的优先级是很重要的。不同的拦截器具有不同的优先级,例如anon拦截器优先级为 1,authc拦截器优先级为 2 等。当某个资源访问同时设置了多个拦截器时,Shiro 会按照优先级顺序依次执行拦截器。比如,对于一个请求路径同时配置了anon和authc拦截器,由于anon优先级更高,会先执行anon拦截器的逻辑,如果不满足anon的条件(即不是匿名可访问的资源),才会继续执行authc拦截器的逻辑。

(三)配置拦截器

在 Spring Boot 中,我们可以通过代码方式配置拦截器,就像前面的 Shiro 配置类中所展示的那样。在ShiroFilterFactoryBean中通过FilterChainDefinitionMap来设置路径与拦截器的对应关系。例如:

Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/logout", "logout");
filterMap.put("/static/**", "anon");
filterMap.put("/user/*", "authc");
filterMap.put("/user/add", "perms(user:add)");
filterMap.put("/admin/*", "roles(admin)");
factory.setFilterChainDefinitionMap(filterMap);

上述代码中,我们定义了不同路径对应的拦截器规则。同时,需要注意的是,在配置路径时可以使用通配符。其中,?匹配一个字符,*匹配零个或多个字符,**匹配零个或多个路径。例如,/user/*可以匹配/user/list、/user/detail等路径;/static/**可以匹配/static/css/style.css、/static/js/app.js等任何位于/static/目录及其子目录下的资源路径。

实现对未登录请求的拦截流程

当用户发送一个请求到应用程序时,Shiro 对未登录请求的拦截流程如下:

  1. 请求进入 ShiroFilter:ShiroFilter 作为安全控制的入口点,会首先拦截所有进入应用的请求。
  2. 匹配过滤规则:ShiroFilter 根据配置的FilterChainDefinitionMap,查找与当前请求路径匹配的过滤规则。例如,如果请求路径是/user/detail,ShiroFilter 会在FilterChainDefinitionMap中查找是否有以/user/detail或/user/*等类似模式匹配的规则。
  3. 执行拦截器逻辑:假设匹配到的规则是authc,则会执行authc拦截器的逻辑。authc拦截器会检查当前用户是否已经登录。如果用户已经登录,则允许请求继续访问目标资源;如果用户未登录,则会根据配置跳转到登录页面。在前面的配置示例中,我们设置了factory.setLoginUrl("/login"),所以此时用户会被重定向到/login路径,提示用户进行登录操作。

自定义 Realm 实现认证和授权

在实际项目中,我们通常需要从数据库或其他数据源获取用户的认证信息(如用户名和密码)以及授权信息(如角色和权限)。这就需要我们自定义 Realm 来实现 Shiro 的认证和授权逻辑。

(一)继承 AuthorizingRealm 类

我们创建一个自定义的 Realm 类,继承自AuthorizingRealm类。AuthorizingRealm类提供了认证和授权的抽象方法,我们需要重写这些方法来实现自己的业务逻辑。以下是一个简单的自定义 Realm 示例:

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Set;

public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    // 授权逻辑:获取用户权限信息
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        // 从数据库查询用户的角色和权限信息
        Set<String> roles = userService.getUserRoles(username);
        Set<String> permissions = userService.getUserPermissions(username);

        info.setRoles(roles);
        info.setStringPermissions(permissions);
        return info;
    }

    // 认证逻辑:验证用户身份
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        User user = userService.findByUsername(username);

        if (user == null) {
            throw new UnknownAccountException("用户不存在");
        }

        return new SimpleAuthenticationInfo(
                user.getUsername(),
                user.getPassword(),
                getName()
        );
    }
}

在上述代码中:

授权方法doGetAuthorizationInfo:该方法在 Shiro 需要验证用户的权限时被调用。首先从PrincipalCollection中获取当前用户的用户名,然后通过调用UserService的方法从数据库中查询该用户所拥有的角色和权限信息。最后,将查询到的角色和权限信息设置到SimpleAuthorizationInfo对象中并返回。

认证方法doGetAuthenticationInfo:当用户尝试登录时,Shiro 会调用该方法进行认证。方法中首先从AuthenticationToken中获取用户名,然后通过UserService从数据库中查询该用户的信息。如果用户不存在,则抛出UnknownAccountException异常;如果用户存在,则创建一个SimpleAuthenticationInfo对象并返回,其中包含了用户的用户名、密码(从数据库中获取的正确密码)以及当前 Realm 的名称。

(二)将自定义 Realm 注入到 SecurityManager

完成自定义 Realm 的编写后,我们需要将其注入到SecurityManager中,以便 Shiro 在进行认证和授权时能够使用我们自定义的逻辑。在 Shiro 配置类中进行如下修改:

@Configuration
public class ShiroConfig {

    // 创建自定义Realm
    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

    // 配置核心安全事务管理器
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    // 配置Shiro过滤器链
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        // 省略其他代码,与前面相同
    }
}

通过上述配置,我们创建了UserRealm的 Bean 实例,并在securityManager的配置方法中,将UserRealm设置到DefaultWebSecurityManager中。这样,Shiro 在进行认证和授权操作时,就会使用我们自定义的UserRealm中的逻辑来处理用户的认证和授权请求。

总结

通过以上步骤,我们详细地介绍了在 Spring Boot3 中整合 Apache Shiro 实现对未登录请求拦截的方法,包括 Shiro 框架的核心组件、整合的准备工作、拦截器的配置、拦截流程以及自定义 Realm 实现认证和授权等内容。在实际项目开发中,各位互联网软件开发人员可以根据项目的具体需求和业务场景,对 Shiro 的配置和自定义 Realm 进行灵活调整和扩展。

例如,在处理复杂的权限管理场景时,可以进一步优化自定义 Realm 中的授权逻辑,支持更细粒度的权限控制,如数据级别的权限控制。同时,还可以结合 Shiro 的缓存机制,提高系统的性能,减少对数据库的频繁查询。希望本文能够对大家在 Spring Boot3 项目中使用 Shiro 实现安全控制提供有力的帮助,让我们的应用程序在安全的道路上不断前行。

相关文章

单打独斗的产品设计师工作流程总结

来人人都是产品经理【起点学院】,BAT实战派产品总监手把手系统带你学产品、学运营。我从入行开始就在一个做自己产品的小公司工作,到现在已经三年了。刚开始工作的时候什么也不懂,老板说让出效果图,就开始直接...

ExpandListView 的一种巧妙写法(三十的另一种写法)

ExpandListView大家估计也用的不少了,一般有需要展开的需求的时候,大家不约而同的都想到了它然后以前自己留过记录的一般都会找找以前自己的代码,没有记录习惯的就会百度、谷歌,这里吐槽一下,好几...

Go语言进阶:时间轮(golang时间轮)

时间轮概念时间轮(Timing Wheel)是一种高效的定时任务调度数据结构,特别适合处理大量定时任务。它通过一个循环数组(轮盘)和多个槽位(buckets)来组织定时任务,每个槽位代表一个时间间隔。...

采用Oracle OSB总线进行服务注册和接入

做大型企业内部业务系统集成的应该都知道,Oracle SOA套件当前是应用广泛的一个商业集成产品套件,其中包括了OSB服务总线, BPEL业务流程引擎,BPM业务流程管理,ODI大数据服务集成,MFT...

超详细的Oracle19c修改数据库用户名教程

概述由于开发很多视图指定了某个用户名,故需修改数据库用户名srmpro为srm。以下为操作过程..1、停止应用防止修改用户名密码后应用一直在发起错误连接,可事先查询哪个IP在连接数据库,然后断开对应连...

Oracle数据库安装 | 步骤详细(oracle数据库怎么安装及配置)

部署环境系统:CentOS 7.1 (Redhat版本也可以)数据库:Oracle 11gR2 1.修改hosts文件1.1 查询主机名和IP地址1.2 修改/etc/hosts文件如下2.挂载操作系...