Spring Boot整合Shiro,两种方式实战总结(含源码)

简介: Spring Boot整合Shiro,两种方式实战总结(含源码)

前言

Shiro是历史悠久的权限管理框,简单易用,易用集成,同时权限管理也是每个项目必不可少的功能。Spring Boot是Java领域炙手可热的脚手架框架。今天这篇文章就带大家将这两个框架进行整合。

通常Spring Boot中整合Shiro,有两种方案:第一,基于原生API进行整合;第二,基于Shiro官方Starter整合。

整体而言,官方Starter整合并没有方便很多,因此,本文主要以原则API进行整合,

下面就来看看具体的整合方式。

创建Spring Boot项目

创建Spring Boot项目通常有两种方式,一种是通过官网创建之后导入到IDE中。一种是通过Idea集成的Spring Boot进行创建。

这里以Idea创建为例,在IDEA中通过Spring Initializr创建项目,在引入依赖时引入Web依赖即可:image.png一路Next,项目创建完成。

引入Shiro依赖

项目创建完成之后,在pom.xml中加入Shiro相关的依赖。关于Shiro的版本信息大家需要留意,尽量使用1.6.0及以上版本。小于1.6.0的版本存在"绕过授权高危漏洞"Bug。

核心的pom依赖内容如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>${shiro.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro.version}</version>
</dependency>

模拟用户查询功能

我们知道在Shiro中可以在Realm实现授权和认证等功能,我们这里采用从数据中查询用户信息,比对密码。所以需要提供一个UserService来实现该功能。

当然,这里只是示例,不建议大家直接在数据库中存储密码。

先创建实体类:

public class User {
    private String username;
    private String password;
    // 省略getter/setter
}

对应的Service:

@Service
public class UserService {
    public User getUserByUserName(String username) {
        // 模拟返回,生产中不建议直接返回明文密码
        User user = new User();
        user.setUsername("secbro");
        user.setPassword("123456");
        return user;
    }
}

自定义Realm并实现功能

这里我们自定义一个Realm,在其中进行认证,关于授权部分默认返回null,也就是不校验权限。具体的权限校验,可根据业务需求,角色权限等设计进行校验。

@Component("loginRealm")
public class LoginRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 这里不做授权校验
        return null;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 用户登录时传入的用户名称
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        // 根据用户名查询用户信息
        User user = userService.getUserByUserName(username);
        if (user == null) {
            throw new UnknownAccountException("账户不存在!");
        }
        if (!password.equals(user.getPassword())) {
            throw new UnknownAccountException("密码错误!");
        }
        return new SimpleAuthenticationInfo(token.getPrincipal(), password, getName());
    }
}

这里直接将LoginRealm通过@Component进行实例化,在doGetAuthenticationInfo方法中进行用户名和密码的认证。

其中核心业务逻辑就是根据用户登录时传入的AuthenticationToken,获得传入的用户名和密码。

然后根据用户名查询数据库存储的用户信息,如果不存在或密码不一致则抛出异常。

最后,返回一个SimpleAuthenticationInfo对象,其中携带用户名、密码信息。

这里我们可以看出来,AuthenticationToken对象中的Principal相当于用户身份(唯一ID),而Credentials相当于密码,用来验证身份的。

集成Shiro配置

创建ShiroConfig类,使用@Configuration注解标注:

@Configuration
public class ShiroConfig {
    /**
     * 此处参数内的Realm已经通过类似的@Compenent进行初始化了
     */
    @Bean
    SecurityManager securityManager(Realm loginRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(loginRealm);
        return manager;
    }
    @Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/index");
        bean.setUnauthorizedUrl("/unauthorizedurl");
        Map<String, String> map = new LinkedHashMap<>();
        map.put("/doLogin", "anon");
        map.put("/**", "authc");
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }
}

其中初始化SecurityManager时需要用到Realm,这里通过方法参数将前面实例的LoginRealm注入,创建SecurityManager时设置对应的Realm。

因为还需要对具体的Web请求进行拦截,因此还需要配置一个ShiroFilterFactoryBean,通过该类指定路径拦截规则,比如设置需要拦截的、不需要拦截的链接等。

其中,Map中配置了路径拦截规则,注意,要有序。

经过上面的配置,Shiro的集成已经完成。

测试案例

下面来写一个简单的Controller来验证一下Shiro的拦截功能。

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        System.out.println("Hello Shiro!");
        return "Hello Shiro!";
    }
    @RequestMapping("/login")
    public String toLogin() {
        return "please login!";
    }
    @RequestMapping("/doLogin")
    public void doLogin(String username, String password) {
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(new UsernamePasswordToken(username, password));
            System.out.println("用户:" + username + ",登录成功!");
        } catch (Exception e) {
            System.out.println("登录异常" + e.getMessage());
        }
    }
}

首先,直接访问/hello接口,由于未登录,直接跳转到/login当中,返回:

please login!

紧接着,访问/doLogin接口,传输用户名和密码,控制台打印:

用户:secbro,登录成功!

再次访问/hello接口,打印并返回:

Hello Shiro!

说明认证成功,并记录下认证成功的状态,后续可继续使用对应的功能了。

基于官方Starter整合

最后再提一下基于官方Starter的整合,其实整体来说只是把ShiroFilterFactoryBean的初始化从配置类移到了application.properties文件中。

首先,将shiro的依赖替换为starter的依赖:

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

然后,去掉ShiroConfig中关于ShiroFilterFactoryBean的实例化方法,改为ShiroFilterChainDefinition:

@Bean
ShiroFilterChainDefinition shiroFilterChainDefinition(){
    DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
    definition.addPathDefinition("/doLogin","anon");
    definition.addPathDefinition("/**","authc");
    return definition;
}

在application.properties文件中添加如下配置:

# 是否允许将sessionId放到cookie中
shiro.sessionManager.sessionIdCookieEnabled=true
# 是否允许将sessionId放到Url地址拦中
shiro.sessionManager.sessionIdUrlRewritingEnabled=true
# 访问未获授权的页面时,默认的跳转路径
shiro.unauthorizedUrl=/unauthorizedurl
# 开启shiro
shiro.web.enabled=true
# 登录成功的跳转页面
shiro.successUrl=/index
# 登录页面
shiro.loginUrl=/login

其他的配置和操作与上面的示例一样,不再赘述。

项目源码:https://github.com/secbr/shiro/tree/main/springboot-shiro

小结

学习Shiro的关键点其实在于理解Realm的使用场景和原理。而关于Shiro在Spring Boot中的集成,如果有不太理解的地方,可回顾一下单纯使用Shiro的示例。Spring Boot的集成只不过是将之前new创建配置项纳入了Spring容器的管理而已。

目录
相关文章
|
5月前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
6月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1079 3
|
7月前
|
安全 Java 数据库
第16课:Spring Boot中集成 Shiro
第16课:Spring Boot中集成 Shiro
926 0
|
4月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
Spring Boot 3.x 微服务架构实战指南
|
4月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
6月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
6月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
8月前
|
Cloud Native Java 微服务
Spring Boot 3.x 现代化应用开发实战技巧与最佳实践
本指南基于Spring Boot 3.x,融合微服务、云原生与响应式编程等前沿技术,打造现代化应用开发实践。通过构建智能电商平台案例,涵盖商品、订单、用户等核心服务,展示Spring WebFlux、OAuth 2.0认证、Spring Cloud Gateway路由、GraalVM原生编译等技术实现。同时提供Docker/Kubernetes部署方案及性能优化策略,助您掌握从开发到生产的全流程。代码示例详实,适合进阶开发者参考。
776 2

热门文章

最新文章