Shiro在SpringBoot中的使用
1. 添加依赖
<!-- Apache Shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version> <!-- 请替换为最新稳定版本 -->
</dependency>
2. 创建 Shiro 相关配置类
@Configuration
public class ShiroConfig {
@Bean
public MyShiroRealm myShiroRealm() {
return new MyShiroRealm();
}
@Bean
protected SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/api/**", "authc");
//或者 全局设置,所有接口都需要登录
// chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
@Bean
@DependsOn("securityManager")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
bean.setLoginUrl("/login");
bean.setUnauthorizedUrl("/unauthorized");
bean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());
return bean;
}
}
3. 创建自定义 Realm 类
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
User user = userService.findByUsername(username);
if (user == null) {
throw new UnknownAccountException("账号不存在");
}
return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 加载用户的权限和角色信息
Set<String> roles = userService.findRolesByUserId(user.getId());
info.setRoles(roles);
Set<String> permissions = userService.findPermissionsByUserId(user.getId());
info.setStringPermissions(permissions);
return info;
}
}
4. 创建登录和注销处理逻辑
@Controller
public class LoginController {
@Autowired
private SubjectService subjectService;
@GetMapping("/login")
public String showLoginForm() {
return "login"; // 返回登录页面
}
@PostMapping("/login")
public String login(String username, String password, Model model) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(token);
return "redirect:/dashboard"; // 登录成功,重定向至首页
} catch (AuthenticationException e) {
model.addAttribute("error", "用户名或密码错误");
return "login"; // 登录失败,返回登录页面
}
}
@GetMapping("/logout")
public String logout() {
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
return "redirect:/login"; // 注销成功,重定向至登录页面
}
}
5. 在 Controller 中进行权限控制
@Controller
public class UserController {
@RequiresPermissions("user:view") // 使用 Shiro 注解进行权限控制
@GetMapping("/users")
public String listUsers(Model model) {
// 只有拥有 "user:view" 权限的用户才能访问该方法
// ...
}
}
6. 页面权限控制(可选)
在 Thymeleaf 视图中,可以通过 Shiro Dialect 进行权限控制:
<a th:href="@{/users}" sec:authorize="hasPermission('user', 'view')">用户管理</a>
详解
Realm
- 定义 Realm:Realm 是 Shiro 中负责安全认证的核心模块,它负责从数据源获取用户、角色和权限信息。这里我们定义一个自定义的
MyShiroRealm
并将其注入为一个 Bean。
@Bean
public MyShiroRealm myShiroRealm() {
return new MyShiroRealm();
}
SecurityManager
- 配置 SecurityManager:SecurityManager 是 Shiro 的核心,所有具体的交互都通过它进行。我们需要将之前定义的 Realm 注入到 SecurityManager 中。
@Bean
@Override
protected SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
Shiro Filter Chain
- 配置 Shiro Filter Chain:定义哪些 URL 需要经过哪些过滤器,例如 "/api/**" 需要经过 "authc" 过滤器,表示这些路径需要认证后才能访问。
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/api/**", "authc");
// 添加更多过滤规则...
return chainDefinition;
}
自定义 Realm 类
MyShiroRealm
类需要实现 AuthorizingRealm
接口,重写 doGetAuthenticationInfo
和 doGetAuthorizationInfo
方法。
-
doGetAuthenticationInfo:这个方法会在用户尝试登录时被调用,用于验证用户的用户名/密码。通常我们会从数据库或其他数据源查询用户信息并返回
AuthenticationInfo
。// 在自定义的 Realm 中实现认证逻辑 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); // 查询数据库或其他数据源获取用户信息 User user = userService.findByUsername(username); if (user == null) { throw new UnknownAccountException("用户不存在"); } // 验证密码,此处假设密码已经过适当的哈希处理 if (!passwordService.validate(user.getPassword(), token.getCredentials())) { throw new IncorrectCredentialsException("密码错误"); } // 返回认证所需信息 return new SimpleAuthenticationInfo(user, user.getPassword(), getName()); }
-
doGetAuthorizationInfo:这个方法会在用户登录成功后被调用,用于获取用户的权限信息。返回
AuthorizationInfo
,包括角色和权限列表。
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roles = userService.findRolesByUserId(user.getId()); // 假设userService能找到用户的角色
info.setRoles(roles);
Set<String> permissions = userService.findPermissionsByUserId(user.getId()); // 假设userService能找到用户的权限
info.setStringPermissions(permissions);
return info;
}
4. 配置 Shiro Filter
在 ShiroFilterChainDefinition 中定义的过滤规则会被注册到 Shiro 的 FilterChainManager 中。例如 "authc" 过滤器意味着必须先通过认证(Authentication)才能访问对应的 URL。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!