SpringBoot浅析安全管理之高级配置

目录
  • 角色继承
  • 动态权限配置
    • 1. 数据库设计
    • 2. 自定义FilterInvocationSecurityMetadataSource
    • 3. 自定义 AccessDecisionManager
    • 4. 配置

角色继承

SpringBoot浅析安全管理之基于数据库认证中定义了三种角色,但是这三种角色之间不具备任何关系,一般来说角色之间是有关系的,例如 ROLE_admin 一般既具有 admin 权限,又具有 user 权限。那么如何配置这种角色继承关系呢?只需要开发者在 Spring Security 的配置类中提供一个 RoleHierarchy 即可

@Bean
RoleHierarchy roleHierarchy() {
    RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
    String hierarchy = "ROLE_dba > ROLE_admin ROLE_admin > ROLE_user";
    roleHierarchy.setHierarchy(hierarchy);
    return roleHierarchy;
}

配置完 RoleHierarchy 之后,具有 ROLE_dba 角色的用户就可以访问 所有资源了,具有 ROLE_admin 角色的用户也可以访问具有 ROLE_user 角色才能访问的资源。

动态权限配置

使用 HttpSecurity 配置的认证授权规则还是不够灵活,无法实现资源和角色之间的动态调整,要实现动态配置 URL 权限,需要开发者自定义权限配置,配置步骤如下传送门

1. 数据库设计

在 10.2节 数据库的基础上再增加一张资源表和资源角色关联表,资源表中定义了用户能够访问的 URL 模式,资源角色表则定义了访问该模式的 URL 需要什么样的角色

建表语句

CREATE TABLE `menu` (
  `id` int(11) NOT NULL,
  `pattern` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `menu_role` (
  `id` int(11) NOT NULL,
  `mid` int(11) DEFAULT NULL,
  `rid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

初始化数据

INSERT INTO `menu`(`id`, `pattern`) VALUES (1, '/db/**');
INSERT INTO `menu`(`id`, `pattern`) VALUES (2, '/admin/**');
INSERT INTO `menu`(`id`, `pattern`) VALUES (3, '/user/**');
INSERT INTO `menu_role`(`id`, `mid`, `rid`) VALUES (1, 1, 1);
INSERT INTO `menu_role`(`id`, `mid`, `rid`) VALUES (2, 2, 2);
INSERT INTO `menu_role`(`id`, `mid`, `rid`) VALUES (3, 3, 3);

Menu 实体类

public class Menu {
    private Integer id;
    private String pattern;
    private List<Role> roles;
    public List<Role> getRoles() {
        return roles;
    }
    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getPattern() {
        return pattern;
    }
    public void setPattern(String pattern) {
        this.pattern = pattern;
    }
}

MenuMapper

@Mapper
public interface MenuMapper {
    List<Menu> getAllMenus();
}

MenuMapper.xml

<?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="org.sang.mapper.MenuMapper">
    <resultMap id="BaseResultMap" type="org.sang.model.Menu">
        <id property="id" column="id"/>
        <result property="pattern" column="pattern"/>
        <collection property="roles" ofType="org.sang.model.Role">
            <id property="id" column="rid"/>
            <result property="name" column="rname"/>
            <result property="nameZh" column="rnameZh"/>
        </collection>
    </resultMap>
    <select id="getAllMenus" resultMap="BaseResultMap">
        SELECT m.*,r.id AS rid,r.name AS rname,r.nameZh AS rnameZh FROM menu m LEFT JOIN menu_role mr ON m.`id`=mr.`mid` LEFT JOIN role r ON mr.`rid`=r.`id`
    </select>
</mapper>

2. 自定义FilterInvocationSecurityMetadataSource

Spring Security 中通过 FilterInvocationSecurityMetadataSource 接口中的 getAttributes 方法来确定一个请求需要哪些角色,FilterInvocationSecurityMetadataSource 接口默认实现类是 DefaultFilterInvocationSecurityMetadataSource ,参考 DefaultFilterInvocationSecurityMetadataSource 的实现,开发者可以定义自己的 FilterInvocationSecurityMetadataSource ,如下:

@Component
public class CustomFilterInvocationSecurityMetadataSource
        implements FilterInvocationSecurityMetadataSource {
    AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Autowired
    MenuMapper menuMapper;
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object)
            throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        List<Menu> allMenus = menuMapper.getAllMenus();
        for (Menu menu : allMenus) {
            if (antPathMatcher.match(menu.getPattern(), requestUrl)) {
                List<Role> roles = menu.getRoles();
                String[] roleArr = new String[roles.size()];
                for (int i = 0; i < roleArr.length; i++) {
                    roleArr[i] = roles.get(i).getName();
                }
                return SecurityConfig.createList(roleArr);
            }
        }
        return SecurityConfig.createList("ROLE_LOGIN");
    }
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

代码解释:

  • 开发者自定义 FilterInvocationSecurityMetadataSource 主要实现接口中的 getAttributes 方法,该方法的参数是一个 FilterInvocation ,开发者可以从 FilterInvocation 中提取当前请求的 URL ,返回值是 Collection,表示当前请求 URL 所需角色
  • 创建一个 AntPathMatcher 主要用来实现 ant 风格的 URL 匹配
  • ((FilterInvocation) object).getRequestUrl(); 从参数中提取当前请求的 URL
  • menuMapper.getAllMenus(); 从数据库中获取所有的资源信息,即 menu 表以及 menu 所对应的 role,在真实项目环境中,开发者可以将资源信息缓存在 Redis 或者其他缓存数据库中
  • 然后遍历资源信息,遍历过程中获取当前请求的 URL 所需要的角色信息并返回。如果当前请求的 URL 在资源表中不存在相应的模式,就假设该请求登录后即可访问,即直接返回 ROLE_LOGIN
  • getAllConfigAttributes 方法用来返回所有定义好的权限资源,Spring Security 在启动时会校验相关配置是否正确,如果不需要校验,那么该方法直接返回 null 即可
  • supports 方法返回 类对象是否支持校验

3. 自定义 AccessDecisionManager

当一个请求走完 FilterInvocationSecurityMetadataSource 中的 getAttributes 方法后,接下来就会来到 AccessDecisionManager 类中进行角色信息的比对,自定义 AccessDecisionManager 如下:

@Component
public class CustomAccessDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication auth,
                       Object object,
                       Collection<ConfigAttribute> ca) {
        Collection<? extends GrantedAuthority> auths = auth.getAuthorities();
        for (ConfigAttribute configAttribute : ca) {
            if ("ROLE_LOGIN".equals(configAttribute.getAttribute())
                    && auth instanceof UsernamePasswordAuthenticationToken) {
                return;
            }
            for (GrantedAuthority authority : auths) {
                if (configAttribute.getAttribute().equals(authority.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足");
    }
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }
    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

代码解释:

  • 自定义 AccessDecisionManager 并重写 decide 方法,在该方法中判断当前登录的用户是否具备当前请求 URL 所需要的角色信息,如果不具备,就抛出 AccessDeniedException 异常,否则不做任何事情
  • decide 有三个参数,第一个参数包含当前登录用户的信息;第二个参数则是一个 FilterInvocation 对象,可以获取当前请求对象等;第三个参数就是 UsernamePasswordAuthenticationToken 的实例,说明当前用户已登录,该方法到此结束,否则进入正常的判断流程,如果当前用户具备当前请求需要的角色,那么方法结束

4. 配置

最后,在 Spring Security 中配置上边的两个自定义类,代码如下:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }
    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        String hierarchy = "ROLE_dba > ROLE_admin ROLE_admin > ROLE_user";
        roleHierarchy.setHierarchy(hierarchy);
        return roleHierarchy;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setSecurityMetadataSource(cfisms());
                        object.setAccessDecisionManager(cadm());
                        return object;
                    }
                })
                .and()
                .formLogin()
                .loginProcessingUrl("/login").permitAll()
                .and()
                .csrf().disable();
    }
    @Bean
    CustomFilterInvocationSecurityMetadataSource cfisms() {
        return new CustomFilterInvocationSecurityMetadataSource();
    }
    @Bean
    CustomAccessDecisionManager cadm() {
        return new CustomAccessDecisionManager();
    }
}

代码解释:

  • 此处 WebSecurityConfig 类的定义是对 10.2节 中 WebSecurityConfig 定义的补充,主要修改了 configure(HttpSecurity http) 方法的实现并添加了两个 Bean
  • object.setSecurityMetadataSource(cfisms()); object.setAccessDecisionManager(cadm());将定义的两个实例设置进去

到此这篇关于SpringBoot浅析安全管理之高级配置的文章就介绍到这了,更多相关SpringBoot高级配置内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot2.0 整合 SpringSecurity 框架实现用户权限安全管理方法

    一.Security简介 1.基础概念 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring的IOC,DI,AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为安全控制编写大量重复代码的工作. 2.核心API解读 1).SecurityContextHolder 最基本的对象,保存着当前会话用户认证,权限,鉴权等核心数据.Secu

  • SpringBoot浅析安全管理之Spring Security配置

    目录 Spring Security 的基本配置 基本用法 1. 创建项目添加依赖 2. 添加 hello 接口 3. 启动项目测试 配置用户名和密码 基于内存的认证 HttpSecurity 登录表单详细配置 注销登录配置 多个 HttpSecurity 密码加密 1. 为什么要加密 2. 加密方案 3. 实践 方法安全 在 Java 开发领域常见的安全框架有 Shiro 和 Spring Security.Shiro 是一个轻量级的安全管理框架,提供了认证.授权.会话管理.密码管理.缓存管理

  • SpringBoot浅析安全管理之OAuth2框架

    目录 OAuth2简介 OAuth2角色 OAuth2授权流程 授权模式 实践 1. 创建项目添加依赖 2. 配置授权服务器 3. 配置资源服务器 4. 配置 Security 5. 验证测试 OAuth2简介 OAuth 是一个开放标准,该标准允许用户让第三方应用访问该用户在某一网站上存储的私密资源(如头像.照片.视频等),而在这个过程中无须将用户名和密码提供给第三方应用. 实现这一功能是通过一个令牌(token),而不是用户名和密码来访问他们存放在特定服务提供者的数据.每一个令牌授权一个特定

  • SpringBoot浅析安全管理之Shiro框架

    目录 Shiro 简介 整合 Shiro 1. 创建项目 2. Shiro基本配置 3. 测试 Shiro 简介 Apache Shiro 是一个开源的轻量级的 Java 安全框架,它提供身份验证.授权.密码管理以及会话管理等功能.相对于 Spring Security ,Shiro 框架更加直观.易用,同时也能提供健壮的安全性. 在传统的 SSM 框架中,手动整合 Shiro 的配置步骤还是比较多的,针对 Spring Boot ,Shiro 官方提供了 shiro-spring-boot-w

  • SpringBoot万字爆肝高级配置

    目录 @ConfigurationProperties注入 创建一个新的模板 ServerConfig.java 编写application.yml 编写ConfigurationApplication.java 给第三方Bean绑定属性 方式一 方式二 @EnableconfigurationProperties 解除使用@ConfigurationProperties注释警告 宽松绑定 常量计量单位绑定 数据校验 进制数据转换规则 @ConfigurationProperties注入 创建一

  • SpringBoot浅析安全管理之基于数据库认证

    目录 1. 设计数据表 2. 创建项目 3. 配置数据库 4. 创建实体类 5. 创建UserService 6. 配置Spring Security 7.创建Controller 8. 测试 1. 设计数据表 用户表.角色表.用户角色关联表 建表语句 CREATE TABLE `role` ( `id` int(11) NOT NULL, `name` varchar(32) DEFAULT NULL, `nameZh` varchar(32) DEFAULT NULL, PRIMARY KE

  • SpringBoot浅析安全管理之高级配置

    目录 角色继承 动态权限配置 1. 数据库设计 2. 自定义FilterInvocationSecurityMetadataSource 3. 自定义 AccessDecisionManager 4. 配置 角色继承 SpringBoot浅析安全管理之基于数据库认证中定义了三种角色,但是这三种角色之间不具备任何关系,一般来说角色之间是有关系的,例如 ROLE_admin 一般既具有 admin 权限,又具有 user 权限.那么如何配置这种角色继承关系呢?只需要开发者在 Spring Secur

  • SpringBoot浅析依赖管理与自动配置概念与使用

    目录 依赖管理 自动版本仲裁 starter启动器 自动配置 说明:基于atguigu学习笔记.部分内容涉及上一章节,请参考以下链接. 上一章:Spring boot 介绍和简易入门 依赖管理 自动版本仲裁 在上一节创建Spring Boot项目时,看到,引入了一个父项目.如下: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-paren

  • SpringBoot浅析缓存机制之Redis单机缓存应用

    目录 Redis单机缓存 1. 创建项目添加缓存依赖 2. 缓存配置 3. 开启缓存 4. 创建 BookDao 5. 创建测试类 Redis单机缓存 和 Ehcache 一样,如果在 classpath 下存在 Redis 并且 Redis 已经配置好了,此时默认就会使用 RedisCacheManager 作为缓存提供者,Redis 单机缓存使用步骤如下: 1. 创建项目添加缓存依赖 创建 Spring Boot 项目,添加 spring-boot-starter-cache 和 Redis

  • SpringBoot浅析缓存机制之Ehcache 2.x应用

    目录 介绍 Ehcache 2.x 缓存 1. 创建项目添加缓存依赖 2. 添加缓存配置文件 3. 开启缓存 4. 创建 BookDao 5 .创建测试类 介绍 Spring 3.1 中开始对缓存提供支持,核心思路是对方法的缓存,当开发者调用一个方法时,将方法的参数和返回值作为 key/value 缓存起来,当再次调用改方法时,如果缓存中有数据,就直接从缓存中获取,否则再去执行该方法.但是,Spring 中并未提供缓存的实现,而是提供了一套缓存 API ,开发者可以自由选择缓存的实现,目前 Sp

  • SpringBoot浅析Redis访问操作使用

    连接操作redis Spring Boot中操作redis还是需要使用相关的启动器 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 按照之前的逻辑 我们还是要分析一下这个启动器的自动配置类RedisAutoConfiguration

随机推荐