首先,在 Maven 项目中,需要在 pom.xml 文件中添加 Shiro 和 Spring Boot Starter 相关的依赖:
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.7.1version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.7.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
接下来,需要在 Spring Boot 中配置 Shiro。可以创建一个 ShiroConfig 类,用于配置 Shiro 的各种组件。
@Configuration
public class ShiroConfig {
@Bean
public SecurityManager securityManager(Realm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
@Bean
public Realm realm() {
// TODO: 实现自己的 Realm
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
这里配置了三个 Bean:
securityManager
:SecurityManager 是 Shiro 的核心组件之一,负责管理所有的 Subject(即用户),并委托 Realm 进行身份认证和权限控制。
realm
:Realm 是 Shiro 的另一个核心组件,负责从数据源中获取用户信息并进行身份认证和授权。需要实现自己的 Realm 类。
shiroFilterFactoryBean
:ShiroFilterFactoryBean 是 Shiro 的 Web 安全组件,负责处理所有的 HTTP 请求并进行安全过滤。这里配置了 URL 模式和安全过滤规则。
authorizationAttributeSourceAdvisor
:AuthorizationAttributeSourceAdvisor 是 Spring AOP 的组件,用于将 Shiro 的权限注解与 Spring AOP 集成。这里配置了用于方法级别权限控制的 Advisor。
上面的 ShiroConfig 中,需要实现自己的 Realm 类。Realm 是一个接口,需要实现其两个核心方法:doGetAuthenticationInfo 和 doGetAuthorizationInfo。
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = (User) principals.getPrimaryPrincipal();
Set<String> roles = userService.getUserRoles(user.getId());
Set<String> permissions = userService.getUserPermissions(user.getId());
authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
User user = userService.getUserByUsername(upToken.getUsername());
if (user == null) {
throw new UnknownAccountException("用户不存在");
}
return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
}
}
在上面的代码中,我们使用了 Spring 的自动注入功能,注入了 UserService,用于从数据源中获取用户信息和权限信息。
doGetAuthenticationInfo 方法用于进行身份认证,它从 AuthenticationToken 中获取用户名和密码,然后调用 UserService 进行验证。
doGetAuthorizationInfo 方法用于进行授权,它从 PrincipalCollection 中获取 User 对象,然后调用 UserService 获取该用户的角色和权限信息,最后将这些信息封装到 AuthorizationInfo 中返回。
接下来,实现 Spring Boot 的 Controller 类,用于处理 HTTP 请求。
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user")
@RequiresPermissions("user:read")
public List<User> getUserList() {
return userService.getUserList();
}
@PostMapping("/user")
@RequiresPermissions("user:create")
public void createUser(@RequestBody User user) {
userService.createUser(user);
}
@PutMapping("/user/{id}")
@RequiresPermissions("user:update")
public void updateUser(@PathVariable("id") Long id, @RequestBody User user) {
user.setId(id);
userService.updateUser(user);
}
@DeleteMapping("/user/{id}")
@RequiresPermissions("user:delete")
public void deleteUser(@PathVariable("id") Long id) {
userService.deleteUser(id);
}
}
在上面的代码中,我们使用了 Shiro 的注解 @RequiresPermissions,用于进行方法级别的权限控制。这里,我们定义了四个接口:获取用户列表、创建用户、更新用户和删除用户,分别对应了不同的权限。
最后,需要编写登录页面和权限不足页面,用于展示登录界面和提示用户权限不足的信息。
登录页面可以通过 Thymeleaf 模板实现,如下所示:
DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>登录title>
<meta charset="UTF-8"/>
head>
<body>
<h2>登录h2>
<form action="/login" method="post">
<div>
<label>用户名:label>
<input type="text" name="username"/>
div>
<div>
<label>密码:label>
<input type="password" name="password"/>
div>
<div>
<button type="submit">登录button>
div>
form>
body>
html>
权限不足页面同样可以通过 Thymeleaf 模板实现,如下所示:
DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>权限不足title>
<meta charset="UTF-8"/>
head>
<body>
<h2>权限不足h2>
<p>对不起,您没有访问该页面的权限。p>
body>
html>
最后,可以通过 Postman 等工具测试接口的访问权限。
首先,访问 /user 接口,应该会跳转到登录页面。
Shiro Login Page
输入正确的用户名和密码后,应该能够成功访问 /user 接口。
Shiro User List
接下来,尝试访问其他接口,如 /user/{id},会发现提示权限不足的错误信息。
Shiro Access Denied
通过上述步骤,我们成功地实现了 Spring Boot 整合 Shiro 实现认证、细粒度方法级别权限控制等功能。Shiro 具有灵活性和可扩展性,可以与 Spring Boot 集成,简化权限控制的实现。