Spring Boot 3.X 整合 Shiro - 登录认证和权限管理
Spring Boot 3.X 整合 Shiro - 登录认证和权限管理
Apache Shiro 是一个强大且易用的 Java 安全框架,提供认证、授权、加密、会话管理等功能。本文将手把手教你在 Spring Boot 3.X 项目中整合 Shiro,实现 安全的登录认证 + 角色权限管理,并且加入生产可用的加密和缓存优化。
1. 添加依赖
在 pom.xml 中添加必要依赖:
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Shiro Spring Boot Starter -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!-- JPA + MySQL -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok(可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2. 数据库建表 SQL
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
salt VARCHAR(50),
status TINYINT DEFAULT 1 COMMENT '0-锁定 1-正常'
);
CREATE TABLE sys_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL UNIQUE,
description VARCHAR(100)
);
CREATE TABLE sys_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
permission VARCHAR(100),
url VARCHAR(200)
);
CREATE TABLE sys_user_role (
user_id BIGINT,
role_id BIGINT,
PRIMARY KEY (user_id, role_id)
);
CREATE TABLE sys_role_permission (
role_id BIGINT,
permission_id BIGINT,
PRIMARY KEY (role_id, permission_id)
);
3. Shiro 核心配置
@Configuration
public class ShiroConfig {
// 密码加密配置
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("SHA-256");
matcher.setHashIterations(1024);
matcher.setStoredCredentialsHexEncoded(true);
return matcher;
}
// 自定义 Realm
@Bean
public CustomRealm customRealm() {
CustomRealm realm = new CustomRealm();
realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
}
// 安全管理器
@Bean
public SecurityManager securityManager(CustomRealm customRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(customRealm);
return securityManager;
}
// 开启 Shiro 注解支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
// Shiro 过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
Map<String, String> map = new LinkedHashMap<>();
map.put("/static/**", "anon");
map.put("/login", "anon");
map.put("/doLogin", "anon");
map.put("/logout", "logout");
map.put("/**", "authc");
factoryBean.setFilterChainDefinitionMap(map);
factoryBean.setLoginUrl("/login");
factoryBean.setSuccessUrl("/index");
factoryBean.setUnauthorizedUrl("/403");
return factoryBean;
}
}
4. 自定义 Realm(安全版)
public class CustomRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(userService.findRoles(username));
info.setStringPermissions(userService.findPermissions(username));
return info;
}
// 认证
@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 (user.getStatus() == 0) throw new LockedAccountException("账号已锁定");
return new SimpleAuthenticationInfo(
user.getUsername(),
user.getPassword(),
ByteSource.Util.bytes(user.getSalt()), // 加盐
getName()
);
}
}
5. 用户服务层
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
public Set<String> findRoles(String username) {
return userRepository.findRolesByUsername(username);
}
public Set<String> findPermissions(String username) {
return userRepository.findPermissionsByUsername(username);
}
}
6. 登录控制器(支持错误提示)
@Controller
public class LoginController {
@GetMapping("/login")
public String loginPage() {
return "login";
}
@PostMapping("/doLogin")
public String doLogin(@RequestParam String username,
@RequestParam String password,
Model model) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
return "redirect:/index";
} catch (UnknownAccountException e) {
model.addAttribute("msg", "用户名错误");
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误");
} catch (LockedAccountException e) {
model.addAttribute("msg", "账号被锁定");
} catch (AuthenticationException e) {
model.addAttribute("msg", "认证失败");
}
return "login";
}
@GetMapping("/logout")
public String logout() {
SecurityUtils.getSubject().logout();
return "redirect:/login";
}
}
7. 权限控制示例
@Controller
public class UserController {
@GetMapping("/index")
public String index() {
return "index";
}
@GetMapping("/admin")
@RequiresRoles("admin")
public String adminPage() {
return "admin";
}
@GetMapping("/user/list")
@RequiresPermissions("user:list")
public String userList() {
return "user/list";
}
}
8. 前端登录页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<div th:if="${msg}" style="color: red;" th:text="${msg}"></div>
<form action="/doLogin" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
9. 生产环境优化建议
- 密码加密:使用 SHA-256 + 盐 + 多次迭代(已在本文配置)。
- 缓存:集成 Redis 缓存角色和权限,避免频繁查库。
- 前后端分离:用自定义 Filter 返回 JSON,而不是跳转页面。
- Remember Me:支持免登录功能,提升体验。
- 会话管理:配置 Shiro SessionManager 或整合 Spring Session。
这样改完后,只需要导入 SQL、运行项目,就能直接体验 Spring Boot 3.x + Shiro 的登录认证和权限管理,而且是 加盐加密版 + 注解控制版 + 可扩展生产版。