Spring Security之授权

2024-01-07 17:49:12

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
Spring Security之授权


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

Spring Security 是一个强大而灵活的安全框架,它为 Spring 应用程序提供了全面的安全解决方案。它旨在简化安全功能的开发和集成,使开发者能够更专注于业务逻辑。通过使用 Spring Security,你可以轻松地实现认证、授权、会话管理、加密等安全功能。
在这一系列博客文章中,我们将深入探讨 Spring Security 的授权部分。授权是指确定用户或角色是否具有访问特定资源或执行特定操作的权限。我们将了解授权的基本概念和原则,学习如何使用 Spring Security 进行授权配置和管理。
通过阅读这些博客文章,你将了解到如何利用 Spring Security 保护你的应用程序,控制对敏感资源的访问,以及实现细粒度的授权控制。我们将提供详细的示例和解释,帮助你逐步理解和应用授权功能。
无论你是一名经验丰富的开发人员还是刚刚开始学习 Spring Security,这个系列的博客文章都将为你提供有价值的信息和实用的技巧。让我们一起探索 Spring Security 之授权的世界,提升应用程序的安全性和保护用户的数据。


提示:以下是本篇文章正文内容,下面案例可供参考

一、RBAC

授权即认证通过后,系统给用户赋予一定的权限,用户只能根据权限访问系统中的某些资源。RBAC是业界普遍采用的授权方式,它有两种解释:

Role-Based Access Control

基于角色的访问控制,即按角色进行授权。比如在学生管理系统中,一个用户拥有老师的角色,那么该用户就可以进行打分和发布作业等一系列老师才能进行操作的权力。

Resource-Based Access Control

基于资源的访问控制,就是按资源或者权限进行授权。现在有一个VIP页面,当你拥有VIP权限后,你才可以进入这个页面。在现在的企业项目开发中,基于资源的控制更加常用。

二、Spring Security之授权

权限表的设计

该表的设计为一个用户拥有多个角色,每个角色拥有不同的权限

CREATE TABLE `users` (
 `uid` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `users` VALUES (1, 'baizhan', '$2a$10$Eqv9PRMl6bPt5BiwgPr2eucgyl.E.xLENt4bvfDvv7DyS5AVPT.U6', '13812345678');


CREATE TABLE `role` (
 `rid` int(11) NOT NULL AUTO_INCREMENT,
 `roleName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `roleDesc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 PRIMARY KEY (`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `role` VALUES (1, '总经理', '管理整个公司');
INSERT INTO `role` VALUES (2, '股东', '参与公司决策');
INSERT INTO `role` VALUES (3, '财务', '管理公司资产');


CREATE TABLE `permission` (
 `pid` int(11) NOT NULL AUTO_INCREMENT,
 `permissionName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `permission` VALUES (1, '查询报表', '/reportform/find');
INSERT INTO `permission` VALUES (2, '查询工资', '/salary/find');
INSERT INTO `permission` VALUES (3, '查询税务', '/tax/find');


CREATE TABLE `users_role` (
 `uid` int(255) NOT NULL,
 `rid` int(11) NOT NULL,
 PRIMARY KEY (`uid`, `rid`) USING BTREE,
 INDEX `rid`(`rid`) USING BTREE,
 CONSTRAINT `users_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
 CONSTRAINT `users_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `users_role` VALUES (1, 2);
INSERT INTO `users_role` VALUES (1, 3);


CREATE TABLE `role_permission` (
 `rid` int(11) NOT NULL,
 `pid` int(11) NOT NULL,
 PRIMARY KEY (`rid`, `pid`) USING BTREE,
 INDEX `pid`(`pid`) USING BTREE,
 CONSTRAINT `role_permission_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
 CONSTRAINT `role_permission_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `permission` (`pid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `role_permission` VALUES (1, 1);
INSERT INTO `role_permission` VALUES (2, 1);
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (3, 2);
INSERT INTO `role_permission` VALUES (1, 3);
INSERT INTO `role_permission` VALUES (2, 3);

编写查询权限方法

1.编写用户、角色、权限实体类

// 不要命名为User,避免和Spring Security提供的User混淆
@Data
public class Users {
  private Integer uid;
  private String username;
  private String password;
  private String phone;
}


// 角色
@Data
public class Role {
  private String rid;
  private String roleName;
  private String roleDesc;
}


// 权限
@Data
public class Permission {
  private String pid;
  private String permissionName;
  private String url;
}

2.编写UserMapper接口

// 根据用户名查询权限
List<Permission> findPermissionByUsername(String username);

3.在resources目录中编写UsersMapper的映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itbaizhan.myspringsecurity.mapper.UsersMapper">
  <select id="findPermissionByUsername" parameterType="string" resultType="com.itbaizhan.myspringsecurity.domain.Permission">
     SELECT DISTINCT permission.pid,permission.permissionName,permission.url FROM
     users
     LEFT JOIN users_role on users.uid = users_role.uid
     LEFT JOIN role on users_role.rid = role.rid
     LEFT JOIN role_permission on role.rid = role_permission.rid
     LEFT JOIN permission on role_permission.pid = permission.pid
     where username = #{username}
  </select>
</mapper>

4.修改认证逻辑,认证成功后给用户授权

// 自定义认证逻辑
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  // 1.构造查询条件
  QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username", username);
  // 2.查询用户
  Users users = userMapper.selectOne(wrapper);
  if (users == null){
    return null;
   }
  // 3.查询用户权限
  List<Permission> permissions = userMapper.findPermissionByUsername(username);
  // 4.将自定义权限集合转为Security的权限类型集合
  List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
  for (Permission permission : permissions) {
    grantedAuthorities.add(new SimpleGrantedAuthority(permission.getUrl()));
   }
  // 5.封装为UserDetails对象
  UserDetails userDetails = User.withUsername(users.getUsername())
     .password(users.getPassword())
     .authorities(grantedAuthorities)
     .build();
  // 6.返回封装好的UserDetails对象
  return userDetails;
}

配置类设置访问控制

1.编写控制器类,添加控制器方法资源

@RestController
public class MyController {
  @GetMapping("/reportform/find")
  public String findReportForm() {
    return "查询报表";
   }


  @GetMapping("/salary/find")
  public String findSalary() {
    return "查询工资";
   }


  @GetMapping("/staff/find")
  public String findStaff() {
    return "查询员工";
   }
}

2.修改Security配置类

// 权限拦截配置
http.authorizeRequests()
     .antMatchers("/login.html").permitAll() // 表示任何权限都可以访问
     .antMatchers("/reportform/find").hasAnyAuthority("/reportform/find") // 给资源配置需要的权限
     .antMatchers("/salary/find").hasAnyAuthority("/salary/find")
     .antMatchers("/staff/find").hasAnyAuthority("/staff/find")
     .anyRequest().authenticated(); //表示任何请求都需要认证后才能访问

自定义访问控制逻辑

如果资源数量庞大,逐个配置所需权限的效率会相对较低。我们可以自定义访问控制逻辑,也就是在访问资源时,判断用户是否拥有与该资源 URL 相对应的权限。

1.自定义访问控制逻辑

@Service
public class MyAuthorizationService {
  // 自定义访问控制逻辑,返回值为是否可以访问资源
  public boolean hasPermission(HttpServletRequest request, Authentication authentication){
    // 获取会话中的登录用户
    Object principal = authentication.getPrincipal();
    if (principal instanceof UserDetails){
      // 获取登录用户的权限
      Collection<? extends GrantedAuthority> authorities = ((UserDetails) principal).getAuthorities();
      // 获取请求的URL路径
      String uri = request.getRequestURI();
      // 将URL路径封装为权限对象
      SimpleGrantedAuthority authority = new SimpleGrantedAuthority(uri);
      // 判断用户的权限集合是否包含请求的URL权限对象
      return authorities.contains(authority);
     }
    return false;
   }
}

2.在配置文件中使用自定义访问控制逻辑

// 权限拦截配置
http.authorizeRequests()
         .antMatchers("/login.html").permitAll() // 表示任何权限都可以访问
        // 任何请求都使用自定义访问控制逻辑
         .anyRequest().access("@myAuthorizationService.hasPermission(request,authentication)");

注解设置访问控制

@Secured

该注解是基于角色的权限控制,要求UserDetails中的权限名必须以ROLE_开头。

1.在主启动类开启注解使用

@SpringBootApplication
@MapperScan("com.itbaizhan.mysecurity.mapper")
@EnableGlobalMethodSecurity(securedEnabled=true)
public class MysecurityApplication {
  public static void main(String[] args) {
    SpringApplication.run(MysecurityApplication.class, args);
   }
}

2.在控制器方法上添加注解

@Secured("ROLE_reportform")
@GetMapping("/reportform/find")
public String findReportForm() {
  return "查询报表";
}

@PreAuthorize

该注解可以在方法执行前判断用户是否具有权限

1.在配置类开启注解使用

@SpringBootApplication
@MapperScan("com.itbaizhan.mysecurity.mapper")
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MysecurityApplication {
  public static void main(String[] args) {
    SpringApplication.run(MysecurityApplication.class, args);
   }
}

2.在控制器方法上添加注解

@PreAuthorize("hasAnyAuthority('/reportform/find')")
@GetMapping("/reportform/find")
public String findReportForm() {
  return "查询报表";
}

权限不足处理方案

1.编写权限不足页面noPermission.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>权限不足</title>
</head>
<body>
<h1>您的权限不足,请联系管理员!</h1>
</body>
</html>

2.编写权限不足处理类

public class MyAccessDeniedHandler implements AccessDeniedHandler {
  @Override
  public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
    response.sendRedirect("/noPermission.html");
   }
}

3.在Spring Security配置文件中配置异常处理

//异常处理
http.exceptionHandling().
        accessDeniedHandler(new MyAccessDeniedHandler());


总结

提示:这里对文章进行总结:

希望这篇博客对你理解和应用 Spring Security 的授权功能有所帮助。如果你有任何问题或想进一步讨论,请随时评论或联系我。

文章来源:https://blog.csdn.net/liubopro666/article/details/135428800
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。