ITPub博客

首页 > 数据库 > 数据库开发技术 > SpringSecurity5.7+最新案例 -- 授权 --

SpringSecurity5.7+最新案例 -- 授权 --

数据库开发技术 作者:CodeSniper 时间:2023-08-07 14:47:03 0 删除 编辑

## 一、前提

书接上回 SpringSecurity5.7+最新案例 -- 用户名密码+验证码+记住我······

本文 继续处理SpringSecurity授权 ......

目前由 难 -> 简,即自定义数据库授权,注解授权,config配置授权

## 二、自定义授权

### 0. 数据准备

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `age` int(11) NULL DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `phone` varchar(13) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `passwd` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `pwd` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `role` json NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, 'a', 35, '太原', '11', '$2a$10$tbFi2xQwHQcjIp4jt5eGZ..CER7ws.Pzg9RiBM1tTGH1omkNprXqC', '123', '[{\"name\": \"系统管理员\", \"role\": \"ROLE_ADMIN\"}, {\"name\": \"普通用户\", \"role\": \"ROLE_USER\"}]');
INSERT INTO `user` VALUES (2, 'b', 51, '沧州', '22', '$2a$10$c8.tVJ36cqcTHV/lGugyc.uf3ft9nQ4jNJpqhhWESpAv7uOspDg1K', '456', '[{\"name\": \"普通用户\", \"role\": \"ROLE_USER\"}]');
INSERT INTO `user` VALUES (3, 'c', 43, '兖州', '33', '$2a$10$akbjbzIjdGjy3/4sUvizae11LvEZXxLUPO0n.4x1NfPupCJYI4aUm', '789', '[{\"name\": \"游客\", \"role\": \"ROLE_GUEST\"}]');
COMMIT;
-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pattern` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `role` json NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of menu
-- ----------------------------
BEGIN;
INSERT INTO `menu` VALUES (1, '/admin/**', '[{\"name\": \"系统管理员\", \"role\": \"ROLE_ADMIN\"}]');
INSERT INTO `menu` VALUES (2, '/user/**', '[{\"name\": \"普通用户\", \"role\": \"ROLE_USER\"}]');
INSERT INTO `menu` VALUES (3, '/guest/**', '[{\"name\": \"普通用户\", \"role\": \"ROLE_USER\"}, {\"name\": \"游客\", \"role\": \"ROLE_GUEST\"}]');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

### 1. 说明

​    由于使用的是jpa,以及在mysql中使用到了json数据,所以先在项目中引入json支持, 使用 mybatis 的绕行

<dependency>

    <groupId>com.vladmihalcea</groupId>

    <artifactId>hibernate-types-52</artifactId>

    <version>2.3.5</version>

</dependency>

### 2. UserDstail变化

0900086000300134184.20201216095126.86523331460016843504112994983392.png

### 3. SecurityConfig中配置变化如下

0900086000300134184.20201216095126.86523331460016843504112994983392.png

### 4. 实现 FilterInvocationSecurityMetadataSource

@Component

public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    // 这里注入的查询menu的接口类

    final MenuRepository menuRepository;

    public MySecurityMetadataSource(MenuRepository menuRepository) {

        this.menuRepository = menuRepository;

    }

    AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override

    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {

        String requestURI = ((FilterInvocation) object).getRequest().getRequestURI();

        // 获取无需权限就能访问的公共资源, 不需权限的访问,直接返回,不需要在进入数据量中查询

        String[] pubSource = PublicRequestSource.get();

        for (String source : pubSource) {

            if (antPathMatcher.match(source, requestURI)) {

                return null;

            }

        }

        // 如果要访问的资源不在公共资源里面,则去数据库中查询,然后创建访问权限

        List<MenuEntity> allMenu = menuRepository.findAll();

        for (MenuEntity menu : allMenu) {

            if (antPathMatcher.match(menu.getPattern(), requestURI)) {

                List<RoleEntity> role = menu.getRole();

                String[] roles = role.stream().map(RoleEntity::getRole).toArray(String[]::new);

                return SecurityConfig.createList(roles);

            }

        }

        return null;

    }

    @Override

    public Collection<ConfigAttribute> getAllConfigAttributes() {

        return null;

    }

    @Override

    public boolean supports(Class<?> clazz) {

        return FilterInvocation.class.isAssignableFrom(clazz);

    }

}

### 5. 配置公共资源  

​    -- 也可以写在配置文件中

public class PublicRequestSource {

    public static String[] get() {

        return new String[]{

                "/hello", "/login"

        };

    }

}

### 6. 实现ObjectPostProcessor

@Component

public class MyObjectPostProcessor implements ObjectPostProcessor<FilterSecurityInterceptor> {

    @Resource

    private MySecurityMetadataSource mySecurityMetadataSource;

    @Override

    public <O extends FilterSecurityInterceptor> O postProcess(O object) {

        object.setSecurityMetadataSource(mySecurityMetadataSource);

        // 打开公共资源访问权限

        object.setRejectPublicInvocations(false);

        return object;

    }

}

### 7. 创建TestController测试请求接口权限

// 实际这里会写在多个controller类下,这里为了测试

@RestController

public class TestController {

    @GetMapping("/admin/hello")

    public String admin() {

        return "hello admin";

    }

    @GetMapping("/user/hello")

    public String user() {

        return "hello user";

    }

    @GetMapping("/guest/hello")

    public String guest() {

        return "hello guest";

    }

    @GetMapping("/hello")

    public String hello() {

        return "hello";

    }

}

### 8. 然后用postman登录测试接口把

0900086000300134184.20201216095126.86523331460016843504112994983392.png

0900086000300134184.20201216095126.86523331460016843504112994983392.png

嗯,其它的就不测试了......

## 三、注解授权

### 1. 开启全局授权

@SpringBootApplication

// 开启全局授权(详细解释如下)

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)

public class SplitBackWeb03Application {

    public static void main(String[] args) {

        SpringApplication.run(SplitBackWeb03Application.class);

    }

}

- **perPostEnabled**: 开启 Spring Security 提供的四个权限注解,@PostAuthorize、@PostFilter、@PreAuthorize 以及@PreFilter。

- **securedEnabled**: 开启 Spring Security 提供的 @Secured 注解支持,该注解不支持权限表达式

- **jsr250Enabled**: 开启 JSR-250 提供的注解,主要是@DenyAll、@PermitAll、@RolesAll 同样这些注解也不支持权限表达式

```

# 以上注解含义如下:

- @PostAuthorize: 在目前标方法执行之后进行权限校验。

- @PostFiter: 在目标方法执行之后对方法的返回结果进行过滤。

- @PreAuthorize:在目标方法执行之前进行权限校验。

- @PreFiter:在目前标方法执行之前对方法参数进行过滤。

- @Secured:访问目标方法必须具各相应的角色。

- @DenyAll:拒绝所有访问。

- @PermitAll:允许所有访问。

- @RolesAllowed:访问目标方法必须具备相应的角色。

```

这些基于方法的权限管理相关的注解,一般来说只要设置 **`prePostEnabled=true`** 就够用了。

### 2. 注意事项

当开启了开启全局授权后,无需额外配置(即SecurityConfig最简如下),当然,也可以和自定义授权一起使用(但不推荐)

httpSecurity.formLogin().and().csrf().disable();

### 3. 创建测试controller类

@RestController

@RequestMapping("/hello")

public class AuthorizeMethodController {

    // 当角色为 ADMIN 并且 认证的用户名为“a”时,才允许访问,否则403

    @PreAuthorize("hasRole('ADMIN') and authentication.name=='a'")

    @GetMapping

    public String hello() {

        return "hello";

    }

    // 当认证的用户名 和 参数中传递的用户名 一致 时,才允许访问,否则403

    @PreAuthorize("authentication.name==#name")

    @GetMapping("/name")

    public String hello(String name) {

        return "hello:" + name;

    }

    // 过滤作用,如传递的UserList id为1,2,3的对象,则后台实际收到的是id为1,3的对象的数据

    // filterTarget 必须是数组 集合 类型

    @PreFilter(value = "filterObject.id%2!=0",filterTarget = "users")

    @PostMapping("/users")

    public void addUsers(@RequestBody List<User> users) {

        System.out.println("users = " + users);

    }

    // 认证作用,当参数中的id=1的时候,可以返回相应的数据,否则403

    @PostAuthorize("returnObject.id==1")

    @GetMapping("/userId")

    public User getUserById(Integer id) {

        return new User(id, "mose");

    }

    // 过滤作用,如结果集中的UserList id为1,2,3的对象,则返回实际的对象只有id 为 2 的对象

    // 返回数据 最好是 数组 或者集合,不然无效

    @PostFilter("filterObject.id%2==0")

    @GetMapping("/lists")

    public List<User> getAll() {

        return userService.findAll();

    }

    // 只能判断角色,当角色为 ROLE_USER 可访问

    @Secured({"ROLE_USER"}) 

    @GetMapping("/secured")

    public User getUserByUsername() {

        return new User(99, "secured");

    }

    // 只能判断角色,当角色具有其中一个即可

    @Secured({"ROLE_ADMIN","ROLE_USER"}) 

    @GetMapping("/username")

    public User getUserByUsername2(String username) {

        return new User(99, username);

    }

    // 允许所有

    @PermitAll

    @GetMapping("/permitAll")

    public String permitAll() {

        return "PermitAll";

    }

    // 拒绝所有

    @DenyAll

    @GetMapping("/denyAll")

    public String denyAll() {

        return "DenyAll";

    }

    // 角色判断,当具有其中一个角色即可

    @RolesAllowed({"ROLE_ADMIN","ROLE_USER"}) 

    @GetMapping("/rolesAllowed")

    public String rolesAllowed() {

        return "RolesAllowed";

    }

}

## 四、配置授权

### 1. 在securityConfig中配置即可,如下

httpSecurity.authorizeHttpRequests()

        .antMatchers("/admin/**").hasRole("ADMIN")

        .antMatchers("/user/**").hasAnyRole("USER", "ADMIN")

        .antMatchers("/guest").hasRole("GUEST")

        .anyRequest().authenticated()

        .and().formLogin()

        .and().csrf().disable();

### 2. 解释

方法 说明
hasAuthority(String authority) 当前用户是否具备指定权限
hasAnyAuthority(String... authorities) 当前用户是否具备指定权限中任意一个
hasRole(String role) 当前用户是否具备指定角色
hasAnyRole(String... roles); 当前用户是否具备指定角色中任意一个
permitAll(); 放行所有请求/调用
denyAll(); 拒绝所有请求/调用
isAnonymous(); 当前用户是否是一个匿名用户
isAuthenticated(); 当前用户是否已经认证成功
isRememberMe(); 当前用户是否通过 Remember-Me 自动登录
isFullyAuthenticated(); 当前用户是否既不是匿名用户又不是通过 Remember-Me 自动登录的
hasPermission(Object targetId, Object permission); 当前用户是否具备指定目标的指定权限信息
hasPermission(Object targetId, String targetType, Object permission); 当前用户是否具备指定目标的指定权限信息

原文作者:;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2018-10-26

  • 博文量
    14
  • 访问量
    1073