当前位置: 首页 > news >正文

使用 Shiro 和 JPA 结合 MySQL 实现一个简易权限管理系统

1. 项目设置

首先,确保你的项目已经配置好 Maven 或 Gradle 依赖管理工具,并添加以下依赖:

Maven 依赖

<dependencies>
    <!-- Shiro 核心库 -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.9.0</version>
    </dependency>

    <!-- Shiro Web 支持 -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-web</artifactId>
        <version>1.9.0</version>
    </dependency>

    <!-- JPA 支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.26</version>
    </dependency>

    <!-- Spring Boot Web 支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Boot 安全支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- 其他依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
</dependencies>

2. 配置 MySQL 数据库

在 application.properties 或 application.yml 中配置 MySQL 数据库连接:

spring.datasource.url=jdbc:mysql://localhost:3306/shiro_demo
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

3. 创建实体类

使用 JPA 创建用户、角色和权限的实体类。

用户实体 (User)
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password;

    @ManyToMany(fetch = FetchType.EAGER)
    private Set<Role> roles;

    // Getters and Setters
}
角色实体 (Role)

@Entity
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToMany(fetch = FetchType.EAGER)
    private Set<Permission> permissions;

    // Getters and Setters
}
权限实体 (Permission)
@Entity
public class Permission {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // Getters and Setters
}

4. 创建 Repository 接口

使用 Spring Data JPA 创建 Repository 接口。

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

public interface RoleRepository extends JpaRepository<Role, Long> {
}

public interface PermissionRepository extends JpaRepository<Permission, Long> {
}

5. 配置 Shiro

创建一个 Shiro 配置类,配置 Realm 和 SecurityManager。

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager(Realm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    @Bean
    public Realm realm(UserRepository userRepository) {
        return new JpaRealm(userRepository);
    }
}

6. 自定义 Realm

创建一个自定义的 Realm 类,用于从数据库中获取用户信息。

public class JpaRealm extends AuthorizingRealm {

    private final UserRepository userRepository;

    public JpaRealm(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        User user = userRepository.findByUsername(username);

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        for (Role role : user.getRoles()) {
            authorizationInfo.addRole(role.getName());
            for (Permission permission : role.getPermissions()) {
                authorizationInfo.addStringPermission(permission.getName());
            }
        }

        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        User user = userRepository.findByUsername(username);

        if (user == null) {
            throw new UnknownAccountException("User not found");
        }

        return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
    }
}

7. 创建控制器

创建一个简单的控制器来处理登录和访问控制。

@Controller
public class HomeController {

    @GetMapping("/home")
    public String home() {
        return "home";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @PostMapping("/login")
    public String doLogin(@RequestParam String username, @RequestParam String password) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            subject.login(token);
            return "redirect:/home";
        } catch (AuthenticationException e) {
            return "redirect:/login?error";
        }
    }

    @GetMapping("/logout")
    public String logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login";
    }
}

8. 创建视图

创建简单的 Thymeleaf 视图来展示登录页面和主页。

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    <form th:action="@{/login}" method="post">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" required>
        <br>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required>
        <br>
        <button type="submit">Login</button>
    </form>
    <p th:if="${param.error}" style="color: red;">Invalid username or password</p>
</body>
</html>

运行 HTML

home.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Home</title>
</head>
<body>
    <h1>Welcome to the Home Page</h1>
    <a th:href="@{/logout}">Logout</a>
</body>
</html>

9. 运行项目

启动 Spring Boot 应用程序,访问 http://localhost:8080/login 进行登录。登录成功后,你将能够访问 /home 页面。

10. 权限控制

你可以在控制器中使用 @RequiresRoles 或 @RequiresPermissions 注解来控制访问权限。

@Controller
public class AdminController {

    @RequiresRoles("admin")
    @GetMapping("/admin")
    public String admin() {
        return "admin";
    }
}

11、创建测试数据脚本

-- 插入权限数据
INSERT INTO permission (name) VALUES ('user:read');
INSERT INTO permission (name) VALUES ('user:write');
INSERT INTO permission (name) VALUES ('admin:read');
INSERT INTO permission (name) VALUES ('admin:write');

-- 插入角色数据
INSERT INTO role (name) VALUES ('user');
INSERT INTO role (name) VALUES ('admin');

-- 关联角色和权限
-- 用户角色拥有 user:read 和 user:write 权限
INSERT INTO role_permissions (role_id, permissions_id) VALUES (
    (SELECT id FROM role WHERE name = 'user'),
    (SELECT id FROM permission WHERE name = 'user:read')
);
INSERT INTO role_permissions (role_id, permissions_id) VALUES (
    (SELECT id FROM role WHERE name = 'user'),
    (SELECT id FROM permission WHERE name = 'user:write')
);

-- 管理员角色拥有所有权限
INSERT INTO role_permissions (role_id, permissions_id) VALUES (
    (SELECT id FROM role WHERE name = 'admin'),
    (SELECT id FROM permission WHERE name = 'user:read')
);
INSERT INTO role_permissions (role_id, permissions_id) VALUES (
    (SELECT id FROM role WHERE name = 'admin'),
    (SELECT id FROM permission WHERE name = 'user:write')
);
INSERT INTO role_permissions (role_id, permissions_id) VALUES (
    (SELECT id FROM role WHERE name = 'admin'),
    (SELECT id FROM permission WHERE name = 'admin:read')
);
INSERT INTO role_permissions (role_id, permissions_id) VALUES (
    (SELECT id FROM role WHERE name = 'admin'),
    (SELECT id FROM permission WHERE name = 'admin:write')
);

-- 插入用户数据
-- 密码使用 Shiro 的加密方式(例如 MD5 加密)
INSERT INTO user (username, password) VALUES ('user1', 'password1');
INSERT INTO user (username, password) VALUES ('admin1', 'password1');

-- 关联用户和角色
-- 用户 user1 拥有 user 角色
INSERT INTO user_roles (user_id, roles_id) VALUES (
    (SELECT id FROM user WHERE username = 'user1'),
    (SELECT id FROM role WHERE name = 'user')
);

-- 用户 admin1 拥有 admin 角色
INSERT INTO user_roles (user_id, roles_id) VALUES (
    (SELECT id FROM user WHERE username = 'admin1'),
    (SELECT id FROM role WHERE name = 'admin')
);

 测试登录

启动应用程序后,使用以下测试用户登录:

  • 用户user1,密码: password1,角色: user

  • 用户admin1,密码: password1,角色: admin

登录后,你可以根据角色和权限访问不同的页面。

相关文章:

  • scratch发射火箭 2024年12月scratch三级真题 中国电子学会 图形化编程 scratch三级真题和答案解析
  • DeepSeek 的开源优势:为什么选择它而不是其他闭源模型?
  • 人工智能 - 大脑神经网络与机器神经网络的区别
  • GitLab CI/CD 的配置详解:从零开始使用 .gitlab-ci.yml 文件
  • 10G EPON光模块
  • 深入解析 vLLM:高性能 LLM 服务框架的架构之美(一)原理与解析
  • CPP集群聊天服务器开发实践(五):nginx负载均衡配置
  • Field ‘id‘ doesn‘t have a default value
  • Redis 事物
  • React 第二十六节 <Profiler></Profiler> 的用途使用方法
  • 【零基础学Mysql】常用函数讲解,提升数据操作效率的利器
  • 2025年 Java 面试八股文
  • 鸿蒙Harmony-UIAbility内状态-LocalStorage详细介绍
  • 【数据结构】 栈和队列
  • Unity DeepSeek API 聊天接入教程(0基础教学)
  • 聊一聊vue如何实现角色权限的控制的
  • JavaBean
  • 【Elasticsearch入门到落地】8、RestClient操作索引库-基础介绍及导入demo
  • 【OpenCV】入门教学
  • Spring 和 Spring MVC 的关系是什么?
  • 江苏银行一季度净赚近98亿增逾8%,不良贷款率微降
  • 美军空袭也门拘留中心,已致68人死亡
  • AI应用大盘点:谁暴涨?谁掉队?
  • “富卫保险冠军赛马日”创双纪录,打造赛马旅游盛宴,印证香港联通国际优势
  • 财政部下达农业生产防灾救灾资金3.76亿元,支持黄淮海等地抗旱保春播
  • 银川市市长信箱被指已读乱回,官方回应