接下来我会详细讲解“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的代码详解 - Python技术站