当前位置:首页 > 技术知识 > 正文内容

Spring Boot 3.X 整合 Shiro - 登录认证和权限管理

maynowei9个月前 (09-13)技术知识99

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. 生产环境优化建议

  1. 密码加密:使用 SHA-256 + 盐 + 多次迭代(已在本文配置)。
  2. 缓存:集成 Redis 缓存角色和权限,避免频繁查库。
  3. 前后端分离:用自定义 Filter 返回 JSON,而不是跳转页面。
  4. Remember Me:支持免登录功能,提升体验。
  5. 会话管理:配置 Shiro SessionManager 或整合 Spring Session。

这样改完后,只需要导入 SQL、运行项目,就能直接体验 Spring Boot 3.x + Shiro 的登录认证和权限管理,而且是 加盐加密版 + 注解控制版 + 可扩展生产版

相关文章

Android主流UI开源库整理(android完整开源项目)

前言最近老大让我整理一份 Android主流UI开源库 的资料,以补充公司的Android知识库。由于对格式不做特别限制,于是打算用博客的形式记录下来,方便查看、防丢并且可以持续维护、不断更新。标题隐...

如何在Go中同步线程(go语言同步锁)

单线程代码已经带来头痛。添加第二个线程,就是从基础头痛升级了。解决方案?互斥锁:线程和数据的交通警察。一旦你理解了它们,线程同步就变成了第二本能,语言无关。在C++和Go中工作,我遇到过所有常见的混乱...

Oracle 11g安装教程完整版(oracle 11g 安装教程)

由于工作需要,将安装的经验分享给大家。第一步:首先准备安装文件包:Oralce 11.2.0.4 64bit和plsqldev1405x64如图所示:第二步:将2个文件解压到同一个目录,如图所示:第三...

Oracle-架构、原理、进程(oracle进程结构)

详解:首先看张图:对于一个数据库系统来说,假设这个系统没有运行,我们所能看到的和这个数据库相关的无非就是几个基于操作系统的物理文件,这是从静态的角度来看,如果从动态的角度来看呢,也就是说这个数据库系统...

Think in Mingdao——人人都是全栈工程师

文/明道云销售部顾问 文静编辑/蒋礼轩一、引言在软件开发领域,有这样一类"Think"系的书籍被广大程序员们奉为经典,如:Think in C++、Think in C#、Think...

每天学Java!Java Bean是什么概念(java bean有什么用)

对于初学Java,或者是刚接触J2EE的人来说,Java bean确实是一个不太好理解的概念,对于一些专业的解释呢,好像看起来也不是那么容易理解。所以小华君今天就准备跟大家说一说Java bean的概...