java中的通用权限管理设计(推荐)

Java中的通用权限管理设计(推荐)

简介

在Java应用程序开发过程中,通用权限管理设计可以有效地管理系统内不同用户的权限,做到安全可靠地管理用户访问数据的安全性和可靠性,避免了系统访问被恶意用户攻击,数据泄露和其他相关问题的出现。

设计

本文推荐一种常见的通用权限管理设计方案,使用RBAC(Role Based Access Control)模型,该模型通过将用户的角色和权限组织成组和子组,使得用户所拥有的所有角色都能够赋予他相应的访问权限。并在访问数据时,针对用户的权限进行数据过滤,保障了数据的安全性。

设计思路

  • 用户(User):指系统内所有的用户。
  • 角色(Role):指是系统内所有可能具有的用户角色,如:管理员、普通用户等。
  • 权限(Permission):系统内所有可以进行操作的权限集合。
  • 资源(Resource):代表被控制的对象,可以是系统内的任何对象或操作,如:一个文件、一个应用、一张表等。
  • 当用户在系统中进行某个操作时,首先会向系统发起申请,系统根据该用户所拥有的角色和权限集合,判断是否具有该操作的访问权限。如果用户拥有访问权限,则可以进行该操作;如果用户没有访问权限,则返回访问失败。

设计实现

在Java程序开发中,可以使用Spring Security框架进行通用权限管理的实现, 其中,Spring Security的核心在于将安全拦截器链绑定到Spring应用程序上,并使用适当的管理器向拦截器链提供身份验证和权限控制信息。

1. 引入Maven依赖

在项目的pom.xml文件中添加Spring Security相关的依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>4.2.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>4.2.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-taglibs</artifactId>
        <version>4.2.3.RELEASE</version>
    </dependency>
</dependencies>

2. 配置Spring Security

在工程中创建 Spring Security 的配置类,继承 WebSecurityConfigurerAdapter 类,并覆盖其中的几个方法:


@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsServiceImpl;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 忽略 /login 接口的 CSRF 验证
        http.csrf().ignoringAntMatchers("/login");

        http.authorizeRequests()
            .antMatchers("/admin").hasRole("ADMIN")
            .antMatchers("/user").hasAnyRole("ADMIN", "USER")
            .anyRequest().permitAll();

        // 配置登录页面和登录接口
        http.formLogin()
            .loginPage("/login").defaultSuccessURL("/")
            .failureUrl("/login?error").permitAll();

        http.logout().logoutSuccessUrl("/login?logout");
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsServiceImpl);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

3. 创建授权服务

在工程中定义 UserDetailsServiceImpl 类,实现 UserDetailsService 接口:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RoleRepository roleRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (null == user) {
            throw new UserNotFoundException("用户不存在");
        }

        Collection<GrantedAuthority> grantedAuthorities = new HashSet<>();
        List<Role> roles = roleRepository.findByUserId(user.getId());
        for (Role role : roles) {
            grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
        }

        return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
    }
}

4. 数据库表结构设计

设计系统中对应的用户、角色和权限表,并通过表与表之间的关联来实现权限管控和角色与权限的逻辑关系。

CREATE TABLE `t_user` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `username` varchar(100) NOT NULL COMMENT '登录名',
    `password` varchar(256) NOT NULL COMMENT '密码',
    `email` varchar(100) NOT NULL COMMENT '邮箱',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

CREATE TABLE `t_role` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `name` varchar(50) NOT NULL COMMENT '角色名称',
    `user_id` bigint(20) NOT NULL COMMENT '用户ID',
    PRIMARY KEY (`id`),
    KEY `idx_user_role` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';

CREATE TABLE `t_permission` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `code` varchar(50) NOT NULL COMMENT '权限编码',
    `name` varchar(50) NOT NULL COMMENT '权限名称',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';

CREATE TABLE `t_role_permission` (
    `role_id` bigint(20) NOT NULL COMMENT '角色ID',
    `permission_id` bigint(20) NOT NULL COMMENT '权限ID',
    PRIMARY KEY (`role_id`, `permission_id`),
    KEY `idx_ROLE_ID` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';

示例

示例1: 演示用户角色授权

在上面的代码示例中,我们定义了 RoleRepository 接口用于操作角色表,定义 PermissionRepository 接口用于操作权限表。下面展示如何定义这两个接口。其中,Role 对应角色表,Permission 对应权限表。

@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {

    List<Role> findByUserId(Long userId);
}

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

在完成实现以上的接口之后,可以修改 UserDetailsServiceImpl 的实现方式,在其中加入角色权限的赋值代码。示例代码如下:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByUsername(username);
    if (null == user) {
        throw new UserNotFoundException("用户不存在");
    }

    Collection<GrantedAuthority> grantedAuthorities = new HashSet<>();
    List<Role> roles = roleRepository.findByUserId(user.getId());
    for (Role role : roles) {
        grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
        List<Permission> permissions = permissionRepository.findByRoleId(role.getId());
        for (Permission permission : permissions) {
            grantedAuthorities.add(new SimpleGrantedAuthority(permission.getCode()));
        }
    }

    return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
}

在以上代码完成之后,可以在代码中分配角色和权限实现访问控制。具体代码示例如下:

// 定义角色列表
List<Role> roles = new ArrayList<>();
Role role = new Role();
role.setName("ADMIN");
role.setUserId(user.getId());
roles.add(role);

// 保存角色
List<Role> savedRoles = roleRepository.saveAll(roles);

// 定义用户权限列表
List<Permission> permissions = new ArrayList<>();
Permission permission = new Permission();
permission.setCode("USER_QUERY");
permission.setName("用户查询");
permissions.add(permission);

// 将权限赋给角色
List<RolePermission> rolePermissions = new ArrayList<>();
for (Role savedRole : savedRoles) {
    for (Permission savedPermission : savedPermissions) {
        RolePermission rolePermission = new RolePermission();
        rolePermission.setPermissionId(savedPermission.getId());
        rolePermission.setRoleId(savedRole.getId());
        rolePermissions.add(rolePermission);
    }
}
rolePermissionRepository.saveAll(rolePermissions);

// 查询分配的权限
List<Role> userRoles = roleRepository.findByUserId(user.getId());
for (Role userRole : userRoles) {
    List<Permission> userPermissions = permissionRepository.findByRoleId(userRole.getId());
    for (Permission permission : userPermissions) {
        System.out.println(permission);
    }
}

示例2: 演示数据过滤

在Spring Security应用程序中,通常可以使用数据过滤技术实现访问控制,以防止用户访问未授权的数据。例如,在一个在线银行系统中,某个用户只能访问自己名下的账户,而不能访问其他用户的账户。下面是一个搭配Spring Data JPA的简单示例:

(1)定义 JPA 查询接口

public interface UserAccountRepositoryCustom {

    List<UserAccount> findByUser(User user);

}

(2)实现 JPA 查询接口

@Component
public class UserAccountRepositoryImpl implements UserAccountRepositoryCustom {

    @Autowired
    UserAccountRepository userAccountRepository;

    @Override
    public List<UserAccount> findByUser(User user) {
        final QUserAccount qUserAccount = QUserAccount.userAccount;
        return new JPAQuery<>(entityManager)
            .from(qUserAccount)
            .where(qUserAccount.user.eq(user))
            .fetch();
    }
}

在以上代码中,QUserAccount 表示JPA查询实体,qUserAccount.user 表示其中的用户字段。

(3)在Service中使用

@Service
@Transactional(readOnly = true)
public class UserAccountServiceImpl implements UserAccountService {

    @Autowired
    UserAccountRepository userAccountRepository;

    @Autowired
    UserAccountRepositoryCustom userAccountRepositoryCustom;

    @Override
    public List<UserAccount> findAll() {
        User user = getLoginUser();
        return userAccountRepositoryCustom.findByUser(user);
    }
}

在以上代码中, getLoginUser() 方法获取当前登录用户信息,通过调用 userAccountRepositoryCustom.findByUser(user) 方法获取当前登录用户账户信息,实现了数据过滤的功能。

小结

本文介绍了一种通用权限管理设计方案,使用RBAC(Role Based Access Control)模型,通过将用户的角色和权限组织成组和子组,实现了对用户权限的控制,并在访问数据时,通过数据过滤实现了对用户数据权限的管控。同时,还给出了在Spring Security框架下实现通用权限管理的实现方式,并提供了代码示例和完整的表结构设计。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java中的通用权限管理设计(推荐) - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Java实现数据库连接池的方法

    Java实现数据库连接池是一种提高性能和应用程序响应能力的有效方法。下面为大家介绍几种常见的Java实现数据库连接池的方法。 使用Apache DBCP实现连接池 Apache DBCP是Java中最常用的开源连接池之一,它是一个开源项目,由Apache软件基金会支持。它使用轻量级语言Java实现,可以通过简单的配置使用。下面是使用Apache DBCP实现…

    Java 2023年5月19日
    00
  • spring boot教程之全局处理异常封装

    Spring Boot教程之全局处理异常封装 在Spring Boot应用程序中,我们经常需要处理各种异常,例如数据库异常、网络异常、业务异常等。为了提高代码的可读性和可维护性,我们可以使用全局异常处理机制来封装和处理异常。本文将详细讲解Spring Boot全局处理异常封装的完整攻略,并提供两个示例。 1. 全局异常处理机制 以下是全局异常处理机制的基本流…

    Java 2023年5月15日
    00
  • Java读取文本文件的各种方法

    下面我将详细讲解“Java读取文本文件的各种方法”的完整攻略。 步骤一:准备文件 首先,我们需要准备一个文本文件,例如 “test.txt”。这个文件可以放在项目目录下,或者使用绝对路径指定其位置。 步骤二:使用Java自带方法读取文本文件 Java提供了自带方法,可以方便地读取文本文件。以下是读取文本文件的代码示例: import java.io.File…

    Java 2023年5月20日
    00
  • Springboot es包版本异常解决方案

    下面是“Springboot es包版本异常解决方案”的完整攻略,包含以下几部分内容: 问题描述 解决方案 示例说明 问题描述 使用 Spring Boot 时,如果要使用 Elasticsearch,一般会使用 Spring Data Elasticsearch(spring-boot-starter-data-elasticsearch),其中包含了 E…

    Java 2023年5月27日
    00
  • asp程序定义变量比不定义变量速度快一倍

    在ASP程序中,定义变量和不定义变量对程序运行的速度有一定的影响。定义变量可以减少内存的开销,提高程序效率,从而让程序运行更快。 一般来说,在ASP程序中,定义变量可以采用如下语法: Dim variable1, variable2, …… 其中,variable1, variable2等表示所定义的变量名,多个变量名之间用逗号隔开。采用这种方式定义…

    Java 2023年6月16日
    00
  • 面试官:怎么做JDK8的垃圾收集器的调优(面试常问)

    下面是关于如何做 JDK8 的垃圾收集器调优的完整攻略: 前言 Java 作为一门高级语言,在垃圾回收上具有很大优势,JDK8 中垃圾收集器不仅越来越多,同时也变得越来越复杂。垃圾收集器调优无疑成为优化 Java 性能的关键),以下将详细介绍如何做JDK8的垃圾收集器调优。 收集器种类 JDK8 中常用的垃圾收集器有以下几种: Serial 收集器:适用于单…

    Java 2023年5月26日
    00
  • java多线程读写文件示例

    下面是关于“Java多线程读写文件”的完整攻略: Java多线程读写文件示例 多线程读取文件 在Java中,可以通过创建多个线程来同时读取文件,以加快文件读取的速度,提高程序的执行效率。下面是一个简单的Java多线程读取文件示例: import java.io.BufferedReader; import java.io.File; import java.…

    Java 2023年5月19日
    00
  • js的表单操作 简单计算器

    下面是一份详细讲解js表单操作的简单计算器的攻略,包含了实现步骤和示例说明。 实现步骤 1. 准备HTML页面 首先,我们需要准备一个HTML页面,用于实现表单操作的简单计算器。在页面中需要包含以下元素: 输入框:用于用户输入数字; 操作符选择框:用于用户选择加、减、乘、除四种操作符; “计算”按钮:用于触发计算操作; 结果展示区:用于展示计算结果。 HTM…

    Java 2023年6月15日
    00
合作推广
合作推广
分享本页
返回顶部