Aurora鉴权部分

Aurora鉴权部分

在认证之后另一个重要的部分就是鉴权,所谓鉴权就是鉴定访问某一方法的用户是否有相应的权限。Spring Security有四种权限控制方法,在Aurora博客中使用的是灵活的动态鉴权,是将权限数据存放在数据库中,而不是以硬编码的形式写入到程序中。

Spring Security的四种权限控制方法
1. 表达式控制 URL 路径权限

Spring Security 支持在 URL 和方法权限控制时使用 SpEL 表达式,如果表达式返回值为 true 则表示需要对应的权限,否则表示不需要对应的权限。SecurityExpressionRoot 类中定义的最基本的 SpEL 有以下部分:

img

在Security的配置类中配置URL路径权限:

protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("admin")
            .antMatchers("/user/**").hasAnyRole("admin", "user")
            .anyRequest().authenticated()
            .and()
            ...
}
2. 表达式控制方法权限

通过在访问的方法上添加表达式注解来控制访问权限。在方法上添加注解控制权限,需要我们首先开启注解的使用,在 Spring Security 配置类上添加如下内容:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    ...
}

这个配置开启了三个注解,分别是:

  • @PreAuthorize:方法执行前进行权限检查
  • @PostAuthorize:方法执行后进行权限检查
  • @Secured:类似于 @PreAuthorize
@Service
public class HelloService {
    @PreAuthorize("principal.username.equals('javaboy')")
    // 只有当前登录用户名为 javaboy 的用户才可以访问该方法。
    public String hello() {
        return "hello";
    }

    @PreAuthorize("hasRole('admin')")
    // 表示访问该方法的用户必须具备 admin 角色。
    public String admin() {
        return "admin";
    }

    @Secured({"ROLE_user"})
    // 该方法的用户必须具备 user 角色,但是注意 user 角色需要加上 ROLE_ 前缀。
    public String user() {
        return "user";
    }

    @PreAuthorize("#age>98")
    // 访问该方法的 age 参数必须大于 98,否则请求不予通过。
    public String getAge(Integer age) {
        return String.valueOf(age);
    }
}
3. 使用过滤注解(这个不太懂,没怎么研究)

Spring Security 中还有两个过滤函数 @PreFilter 和 @PostFilter,可以根据给出的条件,自动移除集合中的元素。

4. 动态权限

动态权限主要通过重写拦截器和决策器来实现,拦截请求,得到请求的url路径,从数据库中查找该url路径需要的角色,将角色放入列表之后传递给决策器。决策器根据用户拥有的角色和需要的角色相对比来决定是否放行。

Aurora鉴权
数据库准备

要实现动态鉴权需要五张表,用户表user,角色表role,资源表resource,用户角色表user_role,角色资源表role_resource。当然在实际项目中大多是先编写好各种基本功能和界面,在前端中初始化分配用户角色和角色权限的,然后再整合上spring security模块。

配置类

动态权限的实现是依靠自己重写的FilterInvocationSecurityMetadataSource 获取资源权限和AccessDecisionManager决策器来实现的。当实现了这两个类之后需要自己配置到Security中:

http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    // fsi FilterInvocation 能够得到访问请求的url和访问方式
                    public <O extends FilterSecurityInterceptor> O postProcess(O fsi) {
                        // 自定义的权限查找器 securityMetadataSource 的作用是找到要访问的资源所需要的权限,如果数据库中没有设置这个url,则返回null,任何用户都可以访问
                        fsi.setSecurityMetadataSource(securityMetadataSource());
                        // 自定义的决策管理器,用于决策是否放行(允许访问资源),在实现中会authentication.getAuthorities()得到用户所有的权限,然后如果
                        // 其中包含所访问资源的其中一个权限就能够放行
                        fsi.setAccessDecisionManager(accessDecisionManager());
                        return fsi;
                    }
                })
SecurityMetadataSource

SecurityMetadataSource 接口负责提供受保护对象所需要的权限。需要继承并实现 FilterInvocationSecurityMetadataSource 接口,然后重写它里边的三个方法:

getAttributes:该方法的参数是受保护对象,在基于 URL 地址的权限控制中,受保护对象就是 FilterInvocation;该方法的返回值则是访问受保护对象所需要的权限。具体的代码和注释如下:

@Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 首先获取所有的资源所需要的角色列表
        if (CollectionUtils.isEmpty(resourceRoleList)) {
            this.loadResourceRoleList();
        }
        FilterInvocation fi = (FilterInvocation) object;
        // 获取请求的方式
        String method = fi.getRequest().getMethod();
        // 获取请求的路径
        String url = fi.getRequest().getRequestURI();
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        // 遍历所有的资源
        for (ResourceRoleDTO resourceRoleDTO : resourceRoleList) {
            // 获取和当前请求路径和请求方法相匹配资源
            if (antPathMatcher.match(resourceRoleDTO.getUrl(), url) && resourceRoleDTO.getRequestMethod().equals(method)) {
                // 获取该资源所需要的角色
                List<String> roleList = resourceRoleDTO.getRoleList();
                // 如果角色为空,那么直接禁止访问
                if (CollectionUtils.isEmpty(roleList)) {
                    return SecurityConfig.createList("disable");
                }
                // 否则返回选哟的角色列表
                return SecurityConfig.createList(roleList.toArray(new String[]{}));
            }
        }
        // 如果没找到对应的资源,返回null,默认返回null可以访问
        return null;
    }
  • getAllConfigAttributes:该方法可以用来返回所有的权限属性,以便在项目启动阶段做校验,如果不需要校验,则直接返回 null 即可。
  • supports:该方法表示当前对象支持处理的受保护对象是 FilterInvocation。
AccessDecisionManager

继承实现AccessDecisionManager实现自己的决策方案类,需要实现decide决策函数,用以判断当前用户的角色是否包含访问资源需要的任意角色,否则抛出权限不足的异常。

@Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        // 获取当前用户所具有的角色列表
        List<String> permissionList = authentication.getAuthorities()
                .stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());
        // 遍历访问资源所需要的角色列表
        for (ConfigAttribute item : collection) {
            // 如果包含任意一个角色那么就准许访问
            if (permissionList.contains(item.getAttribute())) {
                return;
            }
        }
        // 否则抛出异常
        throw new AccessDeniedException("权限不足");
    }

你可能感兴趣的:(aurora博客,java,spring,boot)