详解如何在Spring Security中自定义权限表达式

在对Spring Security中自定义权限表达式的攻略之前,先简单介绍一下权限表达式的作用:

Spring Security中的权限表达式用于在方法调用或请求访问时,判断当前用户是否具有访问权限。Spring Security提供了很多默认的权限表达式,例如"hasRole()"和"hasAnyRole()"等。但是,我们有时需要根据不同的业务需求来自定义权限表达式。下面详细讲解如何在Spring Security中自定义权限表达式。

步骤一:继承SecurityExpressionRoot类

如果我们想要自定义权限表达式,就需要继承Spring Security中的SecurityExpressionRoot类。这个类中包含了Spring Security权限表达式的所有方法。

首先,我们需要建立一个自定义类,例如CustomSecurityExpressionRoot,继承SecurityExpressionRoot。创建完类以后,我们需要在实例化该类时,调用其父类SecurityExpressionRoot的构造方法。

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot {

    public CustomSecurityExpressionRoot(Authentication authentication) {
        super(authentication);
    }

    // 自定义权限表达式

}

步骤二:添加自定义权限表达式的方法

在CustomSecurityExpressionRoot类中,我们需要添加自定义的权限表达式的方法。这些方法应该返回一个Boolean类型的值,根据当前用户是否具有该权限返回True或False。

例如,我们自定义一个权限表达式"hasPermission()",用于判断用户是否已登录并拥有特定权限。具体实现如下:

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot {

    // 代码省略...

    public boolean hasPermission(String permission) {
        Authentication authentication = getAuthentication();
        if (authentication == null) {
            return false;
        }
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        Set<String> authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
        return authorities.contains(permission);
    }

}

上面的代码只是一个简单的示例,它只是检查用户是否有特定权限。您可以根据不同的业务需求,将权限检查更细致地定义。

步骤三:注册自定义的权限表达式

现在我们已经定义了自定义的权限表达式,但是Spring Security并不知道我们的自定义表达式。接下来,我们需要将自定义的权限表达式注册到Spring Security框架中。Spring Security提供了一个ExpressionParser类,用于解析权限表达式。我们需要将自己的表达式注册到ExpressionParser中。

在这里,我们需要创建一个CustomWebSecurityConfig对象,并且将项目所需的AuthenticationProvider以及自定义的CustomSecurityExpressionRoot Bean注入到该对象中。最后,在CustomWebSecurityConfig的configure(HttpSecurity http)方法中,使用http.authorizeRequests()方法调用access()方法,将自定义表达式与URL挂钩起来。

@Configuration
@EnableWebSecurity
public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomSecurityExpressionRoot customSecurityExpressionRoot;

    @Autowired
    private AuthenticationProvider authenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()

            // 自定义权限表达式挂钩
            .antMatchers("/admin/**").access("@customSecurityExpressionRoot.hasPermission('ADMIN')")

            // 其它权限接口
            .antMatchers("/other/**").hasRole("USER")

            .anyRequest().authenticated()
            .and()
            .formLogin()
            .and()
            .httpBasic()
            .and()
            .authenticationProvider(authenticationProvider)
            .csrf().disable();
    }

    @Bean
    public CustomSecurityExpressionRoot customSecurityExpressionRoot(Authentication authentication) {
        return new CustomSecurityExpressionRoot(authentication);
    }

}

上面的代码保护"/admin"路径下的URL,只有管理员具有访问权限。而对于"/other"路径下的URL,则需要用户的角色为"USER"才能访问。

最后是两条示例:

示例1:检查用户是否为特定组织的管理员

public boolean hasOrganizationAdminPermission(String organizationId) {
    Authentication authentication = getAuthentication();
    if (authentication == null) {
        return false;
    }
    UserDetails userDetails = (UserDetails) authentication.getPrincipal();
    Set<String> authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
    String adminPrefix = "ORG_" + organizationId + "_ADMIN_";
    // 如果 authorities 中有以 "ORG_组织ID_ADMIN_" 开头的权限,就说明当前用户是该组织的管理员
    return authorities.stream().anyMatch(permission -> permission.startsWith(adminPrefix));
}

示例2: 检查用户是否属于某个角色(不仅检查角色名,还检查角色所属组织)

public boolean hasRoleWithOrganization(String roleWithOrganization) {
    Authentication authentication = getAuthentication();
    if (authentication == null) {
        return false;
    }
    UserDetails userDetails = (UserDetails) authentication.getPrincipal();
    String[] splitted = roleWithOrganization.split("_");
    if (splitted.length != 3) {
        throw new IllegalArgumentException("roleWithOrganization argument should be in format ROLE_ORG_ROLENAME");
    }
    // Example: ROLE_ORG1_ADMIN -> {ROLE, ORG1, ADMIN}
    String roleName = splitted[2];
    String organization = splitted[1];
    return userDetails.getAuthorities().stream()
            .map(Object::toString)
            .anyMatch(s -> s.equals(roleWithOrganization));
}

将这些方法添加到CustomSecurityExpressionRoot类中,同样使用"registerFunction()"方法将其注册到ExpressionParser中,如下所示:

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot {

    // 代码省略...

    public CustomSecurityExpressionRoot(Authentication authentication) {
        super(authentication);
    }

    // 注册自定义方法
    public void registerFunctions(CustomSecurityExpressionParser parser) {
        parser.registerFunction("hasPermission", 
                new HasPermissionMethod(this));
        parser.registerFunction("hasOrganizationAdminPermission", 
                new HasOrganizationAdminPermissionMethod(this));
        parser.registerFunction("hasRoleWithOrganization", 
                new HasRoleWithOrganizationMethod(this));
    }

}

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解如何在Spring Security中自定义权限表达式 - Python技术站

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

相关文章

  • jQuery EasyUI datagrid在翻页以后仍能记录被选中行的实现代码

    要实现jQuery EasyUI datagrid在翻页以后仍能记录被选中行所对应数据的功能,我们可以通过以下步骤实现: 步骤一:记录选中行的数据 使用EasyUI datagrid中提供的onSelect和onUnselect事件,分别在用户选中和取消选中某一行的时候,记录该行所对应的数据,并将数据存储在一个数组中。具体代码如下: var selected…

    Java 2023年6月15日
    00
  • JDBC连接MySQL5.7的方法

    JDBC是Java语言操作数据库的标准接口,而MySQL是目前最受欢迎的开源数据库之一。在本文中,我们将探讨如何使用JDBC连接MySQL 5.7数据库。 步骤1:下载MySQL JDBC驱动程序 首先,我们需要下载MySQL官方提供的JDBC驱动程序,从而能够在Java应用程序中访问MySQL 5.7数据库。你可以从以下链接下载最新的MySQL JDBC驱…

    Java 2023年6月16日
    00
  • Spring Boot 参数校验的具体实现方式

    下面是 Spring Boot 参数校验的具体实现方式的完整攻略: 第一步:引入依赖 在 pom.xml 中引入依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validat…

    Java 2023年5月20日
    00
  • 解决mybatis-plus通用mapper调用报错:Invalid bound statement

    解决mybatis-plus通用mapper调用报错“Invalid bound statement”的完整攻略如下: 问题背景 在使用mybatis-plus时,常常会使用它提供的通用Mapper进行数据库操作。但是,有时候会出现以下错误报告: org.apache.ibatis.binding.BindingException: Invalid boun…

    Java 2023年5月20日
    00
  • 使用SpringBoot 工厂模式自动注入到Map

    使用SpringBoot工厂模式自动注入到Map的完整攻略 在Spring Boot中,我们可以使用工厂模式将一组相关的类进行组合,并将它们自动注入到Map中。这种方式可以帮助我们更加灵活地管理和使用这些类。本文将详细讲解使用Spring Boot工厂模式自动注入到Map的完整攻略,包括以下内容: 工厂模式的基本概念 Spring Boot中的工厂模式实现方…

    Java 2023年5月15日
    00
  • Java pom.xml parent引用报错问题解决方案

    针对Java pom.xml parent引用报错问题,下面是完整的解决方案攻略。 问题描述 在Maven项目中,我们经常会在子项目的pom.xml文件中引用父项目的依赖或配置信息。通常使用<parent>元素引用父pom.xml文件的配置。但是,在实际开发过程中,我们可能会遇到以下错误: Project build error: Non-res…

    Java 2023年5月19日
    00
  • Spring Data JPA系列JpaSpecificationExecutor用法详解

    Spring Data JPA系列JpaSpecificationExecutor用法详解 什么是 JpaSpecificationExecutor JpaSpecificationExecutor 是 Spring Data JPA 提供的一个接口,它提供了使用 JPA Criteria API 进行查询、分页、排序等操作的方法。在 Repository …

    Java 2023年6月2日
    00
  • SpringBoot 的 web 类型推断详解

    Spring Boot是一个快速开发框架,可以帮助开发人员快速构建Web应用程序。在开发过程中,经常需要处理HTTP请求和响应。为了简化开发,Spring Boot提供了Web类型推断功能,可以自动推断HTTP请求和响应的类型。本文将介绍Spring Boot的Web类型推断功能,并提供两个示例。 什么是Web类型推断? Web类型推断是Spring Boo…

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