SpringBoot整合Shiro和Redis的示例代码

下面我将为你详细讲解“SpringBoot整合Shiro和Redis的示例代码”的具体过程,包含示例代码说明。

一、引入相关依赖

首先需要在 pom.xml 文件中引入相关依赖,包括 SpringBoot、Shiro 和 Redis 的依赖,示例代码如下:

<dependencies>
    <!-- SpringBoot 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>

    <!-- Shiro 依赖 -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-starter</artifactId>
        <version>1.6.0</version>
    </dependency>

    <!-- Redis 客户端依赖 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.8.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.8.0</version>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.2.0</version>
    </dependency>
</dependencies>

二、配置 Shiro

application.yml(或 application.properties) 文件中配置 Shiro 的相关信息,示例代码如下:

shiro:
  hashAlgorithmName: md5 # 修改 hash 算法,默认是 sha-256
  hashIterations: 2 # 修改 hash 迭代次数
  redis:
    host: localhost
    port: 6379
    timeout: 10000
    password: XXXXXX
    database: 0

其中,hashAlgorithmNamehashIterations 是用来加密密码的,这里以 md5 和 2 为例。

三、编写 Shiro 自定义 Realm

需要自定义一个 Realm 来将 Shiro 和 Redis 集成起来。示例代码如下:

public class CustomRealm extends AuthorizingRealm {

    private RedisManager redisManager;

    public void setRedisManager(RedisManager redisManager) {
        this.redisManager = redisManager;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(redisManager.getRoles(username));
        authorizationInfo.setStringPermissions(redisManager.getPermissions(username));
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        String dbPassword = redisManager.getPassword(username);
        if (dbPassword == null || !dbPassword.equals(password)) {
            throw new IncorrectCredentialsException("用户名或密码错误!");
        }
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

四、编写 RedisManager

RedisManager 是用来访问 Redis 缓存的,示例代码如下:

public class RedisManager {

    private JedisPool jedisPool;

    public RedisManager(String host, int port, int timeout, String password, int database) {
        jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout, password, database);
    }

    public Jedis getJedis() {
        return jedisPool.getResource();
    }

    public String getPassword(String username) {
        try (Jedis jedis = getJedis()) {
            return jedis.hget("user:" + username, "password");
        }
    }

    public Set<String> getRoles(String username) {
        try (Jedis jedis = getJedis()) {
            return jedis.smembers("user:" + username + ":roles");
        }
    }

    public Set<String> getPermissions(String username) {
        try (Jedis jedis = getJedis()) {
            return jedis.smembers("user:" + username + ":permissions");
        }
    }
}

五、集成 Shiro 和 Redis

这里使用 Shiro 和 Redis 的集成插件 ShiroRedisCacheManager

@Configuration
public class ShiroConfig {

    @Bean
    public CustomRealm customRealm() {
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCachingEnabled(true);
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        customRealm.setAuthenticationCachingEnabled(true);
        customRealm.setAuthenticationCacheName("authenticationCache");
        customRealm.setAuthorizationCachingEnabled(true);
        customRealm.setAuthorizationCacheName("authorizationCache");
        customRealm.setRedisManager(redisManager());
        return customRealm;
    }

    @Bean
    public RedisManager redisManager() {
        return new RedisManager(
                environment.getProperty("shiro.redis.host"),
                Integer.valueOf(environment.getProperty("shiro.redis.port")),
                Integer.valueOf(environment.getProperty("shiro.redis.timeout")),
                environment.getProperty("shiro.redis.password"),
                Integer.valueOf(environment.getProperty("shiro.redis.database"))
        );
    }

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName(environment.getProperty("shiro.hashAlgorithmName"));
        matcher.setHashIterations(Integer.valueOf(environment.getProperty("shiro.hashIterations")));
        return matcher;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");

        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(customRealm());
        securityManager.setCacheManager(redisCacheManager());
        return securityManager;
    }

    @Bean
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager redisCacheManager = new ShiroRedisCacheManager(redisManager());
        redisCacheManager.setKeyPrefix("shiro:cache:");
        redisCacheManager.setExpire(1800); // 设置缓存过期时间,单位秒,默认1小时
        return redisCacheManager;
    }

}

六、示例说明

示例1:验证用户登录

首先创建一个账号密码为 test/test123 的用户,在该账户下分配两个角色 adminuser,每个角色各有两个权限 user:adduser:delete

在前端页面进行登录操作,后台服务器验证用户名和密码。如果验证成功,CustomRealm#doGetAuthorizationInfo() 方法将被调用,用户的角色和权限将存储在 Redis 中。

@Controller
public class LoginController {

    @RequestMapping("/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        Model model) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token);
            return "redirect:/index";
        } catch (Exception e) {
            model.addAttribute("errorMsg", "用户名或密码错误!");
            return "login";
        }
    }

}

示例2:查询用户权限

在前端页面进行某个操作时会访问后端服务,后端服务需要验证该用户是否有权限进行该操作。由于用户权限已经存储在 Redis 中,直接从 Redis 中获取即可。

@RequestMapping("/operation")
@ResponseBody
public String operation() {
    Subject subject = SecurityUtils.getSubject();
    if (subject == null || !subject.isAuthenticated()) {
        return "用户未登录或登录状态已过期!";
    }
    if (subject.isPermitted("user:add")) {
        return "用户具有添加权限!";
    } else {
        return "用户没有添加权限!";
    }
}

以上便是 SpringBoot 整合 ShiroRedis 的完整攻略。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot整合Shiro和Redis的示例代码 - Python技术站

(0)
上一篇 2023年6月15日
下一篇 2023年6月15日

相关文章

  • Java集合Stream流操作的基本使用教程分享

    Java集合Stream流操作的基本使用教程分享 什么是Java集合Stream流? Java集合Stream流是Java 8新增的一个处理集合数据的API。集合Stream流本质上是一个“管道”或者“流水线”,它可以通过一系列中间操作对数据进行处理。中间操作不会导致数据计算,只会记录操作,而最终的操作称为终端操作,会触发所有中间操作的计算并返回一个结果。 …

    Java 2023年5月26日
    00
  • Springboot处理异常的常见方式

    在Springboot中,异常处理是一个非常重要的话题。对于Web应用程序来说,它尤其重要,因为在 Web 应用程序中,您需要处理各种类型的异常,并向客户端发送有意义的响应。本文将为您介绍在Springboot中处理异常的常见方式。 异常处理的概念 异常处理:所谓异常处理,就是在应用程序执行出错时,能够捕获到错误并对其进行处理,让应用程序继续运行的一种技术。…

    Java 2023年5月27日
    00
  • Java实现归并排序的示例代码

    针对Java实现归并排序的示例代码,我来进行详细讲解,包括一些示例代码的说明。 归并排序简介 归并排序是一种基于分治思想的排序算法。其基本思想是将待排序序列拆分成若干子序列,分别进行排序,最后合并子序列,得到最终有序序列。具体来说,归并排序将待排序数组分为两个部分,分别对两个部分进行递归排序,将排好序的两个部分合并成一个有序序列。时间复杂度是O(n logn…

    Java 2023年5月19日
    00
  • 一文带你了解Java中的Object类及类中方法

    一文带你了解Java中的Object类及类中方法 什么是Object类? 在Java中,所有的类都继承自Object类,Object类是Java中所有类的祖先类,其定义了所有类都有的基本方法。 Object类中的常用方法 equals()方法 equals()方法用于判断两个对象是否相等。如果两个对象的内容相同,equals方法返回true,否则返回fals…

    Java 2023年5月26日
    00
  • java算法Leecode刷题统计有序矩阵中的负数

    Java算法Leetcode刷题是大多数Java程序员在提高自己的算法能力时所选择的途径之一。其中,《有序矩阵中的负数》是一道常见的算法题目。下面我将给出一份完整攻略,以便Java程序员能够更好地掌握这道题目。 题目描述 给定一个m*n的矩阵grid,其中每行和每列均已按非递增顺序排好序,请你统计并返回grid中 负数 的个数。 解题思路 因为矩阵已按照非递…

    Java 2023年5月19日
    00
  • SpringBoot整合Mybatis注解开发的实现代码

    接下来我将以以下步骤为例,详细讲解SpringBoot整合Mybatis注解开发的实现代码: 配置Mybatis 首先,在Spring Boot配置文件中添加Mybatis的相关配置,如下所示: mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to…

    Java 2023年5月20日
    00
  • java web图片上传和文件上传实例

    下面是关于“Java Web文件上传和图片上传实例”的攻略及示例。 一、文件上传和图片上传的区别 文件上传和图片上传本质上类似,都是将本地文件上传到服务器的某个文件夹中。但是,图片上传还需要进行图片预览和显示操作,所以相较于文件上传,图片上传多了一些处理操作。 二、Java Web实现文件上传和图片上传 在Java Web中,文件上传和图片上传的核心是使用M…

    Java 2023年5月19日
    00
  • 基于Java生成GUID的实现方法

    基于Java生成GUID的实现方法 GUID(即全局唯一标识符)是一种算法,用于在计算机系统中生成唯一的标识符。本文将介绍在Java中生成GUID的实现方法。 UUID类 Java提供了一个UUID类,可以用于生成GUID。UUID是一个128位数字,通常用32个十六进制数表示。它有几个版本,其中最常用的是版本4(在Java中对应的是randomUUID()…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部