SpringBoot整合Shiro的代码详解

接下来我会详细讲解“SpringBoot整合Shiro的代码详解”的完整攻略。整个过程分为以下几个步骤:

  1. 添加依赖
  2. 配置Shiro
  3. 编写身份认证和授权逻辑
  4. 添加Web接口
  5. 测试

下面我会一一解释每个步骤的具体内容。

1. 添加依赖

首先需要在pom.xml文件中添加Shiro和SpringBoot的依赖:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.5.3</version>
</dependency>

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

2. 配置Shiro

接下来需要在SpringBoot的配置文件application.properties中添加Shiro的相关配置:

# 设置Shiro的缓存为Redis
shiro.redis.enabled=true
# Redis主机地址
shiro.redis.host=localhost
# Redis主机端口
shiro.redis.port=6379
# Redis密码
shiro.redis.password=
# session过期时间,单位为毫秒
shiro.redis.session-expire=1800000

然后在SpringBoot的启动类Application.java中添加Shiro的配置:

@Configuration
public class ShiroConfig {

    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(env.getProperty("shiro.redis.host"));
        redisManager.setPort(Integer.parseInt(env.getProperty("shiro.redis.port")));
        redisManager.setPassword(env.getProperty("shiro.redis.password"));
        redisManager.setExpire(Integer.parseInt(env.getProperty("shiro.redis.session-expire")));
        return redisManager;
    }

    @Bean
    public RedisCacheManager cacheManager(RedisManager redisManager) {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager);
        return redisCacheManager;
    }

    @Bean
    public SessionDAO sessionDAO(RedisManager redisManager) {
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        sessionDAO.setCacheManager(cacheManager(redisManager));
        return sessionDAO;
    }

    @Bean
    public DefaultWebSecurityManager securityManager(UserRealm userRealm, SessionDAO sessionDAO) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setSessionManager(defaultWebSessionManager(sessionDAO));
        return securityManager;
    }

    @Bean
    public DefaultWebSessionManager defaultWebSessionManager(SessionDAO sessionDAO) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(sessionDAO);
        return sessionManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilter;
    }
}

其中,RedisManagerRedisCacheManagerSessionDAODefaultWebSecurityManagerDefaultWebSessionManagerShiroFilterFactoryBean都是Shiro提供的Bean,具体作用可以参考Shiro的官方文档。

3. 编写身份认证和授权逻辑

接下来需要编写身份认证和授权逻辑。可以通过继承Shiro提供的AuthorizingRealmAuthenticationRealm类来实现。

public class UserRealm extends AuthorizingRealm {

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        User user = (User) principals.getPrimaryPrincipal();
        authorizationInfo.setRoles(user.getRoles());
        authorizationInfo.setStringPermissions(user.getPermissions());
        return authorizationInfo;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        String username = userToken.getUsername();
        String password = new String(userToken.getPassword());

        // 查询数据库中是否有该用户
        User user = userDao.getUserByUsername(username);
        if (user == null) {
            throw new UnknownAccountException("用户名或密码错误");
        }
        if (!password.equals(user.getPassword())) {
            throw new IncorrectCredentialsException("用户名或密码错误");
        }

        // 认证成功,返回身份信息
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, password, getName());
        return authenticationInfo;
    }
}

上面的代码中,doGetAuthorizationInfo方法用于授权,doGetAuthenticationInfo方法用于身份认证。其中,User对象代表当前用户的身份信息,在Shiro中被称为“Principal”。

4. 添加Web接口

接下来需要添加Web接口,提供访问控制的功能。可以通过SpringBoot的@Controller@RequestMapping注解来实现。

@Controller
public class UserController {

    @RequestMapping("/login")
    public String login(String username, String password) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            return "index";
        } catch (UnknownAccountException e) {
            return "login";
        } catch (IncorrectCredentialsException e) {
            return "login";
        } catch (AuthenticationException e) {
            return "login";
        }
    }

    @RequestMapping("/unauthorized")
    public String unauthorized() {
        return "unauthorized";
    }

    @RequestMapping("/logout")
    public String logout() {
        Subject subject = SecurityUtils.getSubject();
        if (subject.isAuthenticated()) {
            subject.logout();
        }
        return "login";
    }

    @RequestMapping("/")
    @RequiresPermissions("user:view")
    public String index() {
        return "index";
    }

    @RequestMapping("/list")
    @RequiresPermissions("user:list")
    public String list() {
        return "list";
    }
}

上面的代码中,@RequiresPermissions注解表示需要该权限才能访问该接口。

5. 测试

最后,需要对整个应用进行测试。可以通过启动SpringBoot应用,然后访问相关接口来进行测试。以下是一个测试的示例:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {

    private static final String username = "admin";
    private static final String password = "123456";

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void testLogin() {
        ResponseEntity<String> response = restTemplate.postForEntity("/login?username={username}&password={password}", null, String.class, username, password);
        Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
        Assert.assertEquals("index", response.getBody());
    }

    @Test
    public void testUnauthorized() {
        ResponseEntity<String> response = restTemplate.getForEntity("/unauthorized", String.class);
        Assert.assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
    }

    @Test
    public void testLogout() {
        ResponseEntity<String> response = restTemplate.getForEntity("/logout", String.class);
        Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
        Assert.assertEquals("<html><body>欢迎登录</body></html>", response.getBody());
    }

    @Test
    public void testIndex() {
        ResponseEntity<String> response = restTemplate.getForEntity("/", String.class);
        Assert.assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());

        response = restTemplate.postForEntity("/login?username={username}&password={password}", null, String.class, username, password);
        Assert.assertEquals(HttpStatus.OK, response.getStatusCode());

        response = restTemplate.getForEntity("/", String.class);
        Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
        Assert.assertEquals("<html><body>首页</body></html>", response.getBody());
    }

    @Test
    public void testList() {
        ResponseEntity<String> response = restTemplate.getForEntity("/list", String.class);
        Assert.assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());

        response = restTemplate.postForEntity("/login?username={username}&password={password}", null, String.class, username, password);
        Assert.assertEquals(HttpStatus.OK, response.getStatusCode());

        response = restTemplate.getForEntity("/list", String.class);
        Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
        Assert.assertEquals("<html><body>列表页</body></html>", response.getBody());
    }
}

上面的测试代码中,使用了SpringBoot提供的TestRestTemplate来进行接口测试。

这样就完成了整个应用的搭建和测试。

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

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

相关文章

  • java框架基础之SPI机制实现及源码解析

    Java框架基础之SPI机制实现及源码解析 什么是SPI机制 SPI(Service Provider Interface)是Java提供的一套面向接口的类加载机制。简单来说,我们可以通过SPI机制来动态替换掉接口的默认实现。 在Java中,我们通常提供一个接口,并给出一个接口的默认实现。而在SPI中,我们则可以提供一个接口,以及多个不同实现该接口的类。在程…

    Java 2023年5月26日
    00
  • 详解Maven打包和运行

    下面我将为你详细讲解Maven打包和运行的完整攻略。该攻略包含以下几个部分: 环境准备与Maven安装 Maven项目配置 打包操作 运行操作 先来看第一部分——环境准备与Maven安装。 环境准备与Maven安装 在进行Maven打包和运行之前,我们需要对环境进行一些准备工作: 安装Java环境:Maven需要依赖Java环境,如果你还没有安装Java环境…

    Java 2023年5月20日
    00
  • 不同Java泛型构造函数的详解

    不同Java泛型构造函数的详解 在Java中,泛型构造函数是指可以带有一个或多个类型参数的构造函数。泛型构造函数有助于开发人员在编写代码时提高代码的重用性和可读性。 泛型构造函数语法 泛型构造函数的语法非常简单,只需要将构造函数名称放在尖括号中,并在其中指定一个或多个类型参数。例如: public class MyClass<T> { publi…

    Java 2023年5月26日
    00
  • JAVA 内部类详解及实例

    JAVA 内部类详解及实例 Java内部类可以看作是一种定义在另一个类内部的类。它们有访问外部类的所有成员的权限。Java内部类可以分为四种:成员内部类、局部内部类、匿名内部类和静态内部类。 成员内部类 成员内部类定义在外部类的内部,并且不是static类型的。成员内部类可以直接访问外部类的成员变量和方法,并且可以通过this关键字访问自己的成员变量和方法。…

    Java 2023年5月26日
    00
  • 详解SpringBoot中JdbcTemplate的事务控制

    详解SpringBoot中JdbcTemplate的事务控制 什么是JdbcTemplate JdbcTemplate是Spring框架提供的一个简化JDBC操作的模板类,通过JdbcTemplate可以避免传统JDBC操作中大量重复的样板代码,提高开发效率。JdbcTemplate中封装了大量常用操作方法,如查询、更新等。 什么是事务控制 事务是指具有原子…

    Java 2023年5月20日
    00
  • Springboot使用Logback实现日志配置与异常记录

    Spring Boot使用Logback实现日志配置与异常记录 介绍 Spring Boot是一款轻量级的应用框架,它提供了很多有用的功能来简化应用开发流程,其中包括了日志记录功能。Logback是一个优秀的日志框架,它可以取代Java标准库的日志框架,并支持通过XML文件配置日志。在这篇教程中,我们将看到如何在Spring Boot应用中使用Logback…

    Java 2023年5月25日
    00
  • SpringMVC中Controller类数据响应的方法

    下面是SpringMVC中Controller类数据响应的方法的完整攻略。 什么是Controller Controller负责处理来自用户的请求,并将处理结果返回给用户。在SpringMVC中,Controller是一个Java类,并使用@Controller注解来标识。 Controller类数据响应的方法 在Controller中,数据响应的方法有很多…

    Java 2023年6月15日
    00
  • Java实战房屋租赁网的实现流程

    以下是我对于Java实战房屋租赁网的实现流程的详细讲解: 实现流程 1. 需求分析 在开始实现之前,需要进行需求分析,确定网站的主要功能和用户需求,以便于更好地实现网站。需要考虑以下问题: 用户需要哪些功能?例如:房屋的浏览、搜索、下单、支付等功能。 网站需要哪些信息?例如:用户信息、房屋信息、订单信息等。 网站的业务流程是怎样的?例如:用户搜索房屋-&gt…

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