接下来我会详细讲解“SpringBoot整合Shiro的代码详解”的完整攻略。整个过程分为以下几个步骤:
接下来我会详细讲解“SpringBoot整合Shiro的代码详解”的完整攻略。整个过程分为以下几个步骤:
- 添加依赖
 - 配置Shiro
 - 编写身份认证和授权逻辑
 - 添加Web接口
 - 测试
 
下面我会一一解释每个步骤的具体内容。
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;
    }
}
其中,RedisManager、RedisCacheManager、SessionDAO、DefaultWebSecurityManager、DefaultWebSessionManager、ShiroFilterFactoryBean都是Shiro提供的Bean,具体作用可以参考Shiro的官方文档。
3. 编写身份认证和授权逻辑
接下来需要编写身份认证和授权逻辑。可以通过继承Shiro提供的AuthorizingRealm和AuthenticationRealm类来实现。
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的代码详解
				
        
 
            
        - Java超详细讲解排序二叉树 2022-11-29
 - 解决@ConfigurationProperties注解的使用及乱码问题 2023-06-24
 - SpringMVC @GetMapping注解路径冲突问题解决 2023-02-27
 - SpringMVC执行过程详细讲解 2023-05-08
 - SpringBoot2.X整合Spring-Cache缓存开发的实现 2023-03-16
 - Spring boot 运用策略模式实现避免多次使用if的操作代码 2023-04-23
 - Java static关键字详细解析 2023-04-23
 - jsp中页面间传汉字参数转码的方法 2023-12-10
 - 关于MyBatis中映射对象关系的举例 2023-01-24
 - java – 我可以在客户端 – 服务器数据库环境中使用嵌入式Derby数据库吗? 2023-11-04
 
						
						
						
						
						
				
				
				
				