自定义安全表达式

huangapple go评论73阅读模式
英文:

Custom Security Expression

问题

我正在学习如何创建自定义安全表达式的教程,我创建了三个类,但是我遇到了错误,我尝试了谷歌上的一切,可能是我没有更新或其他原因。你能解释一下发生了什么吗?

错误信息:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: Failed to evaluate expression 'isComprador()'] with root cause

方法调用:在类型 org.springframework.security.access.expression.method.MethodSecurityExpressionRoot 上找不到方法 isComprador()

MethodSecurityConfig 类:
```java
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new CustomMethodSecurityExpressionHandler();
    }
}

CustomMethodSecurityExpressionHandler 类:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
        CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(this.trustResolver);
        root.setRoleHierarchy(getRoleHierarchy());
        return root;
    }
}

CustomMethodSecurityExpressionRoot 类:

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    private Object filterObject;
    private Object returnObject;
    private Object target;

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

    @Override
    public void setFilterObject(Object filterObject) {
        this.filterObject = filterObject;
    }

    @Override
    public Object getFilterObject() {
        return filterObject;
    }

    @Override
    public void setReturnObject(Object returnObject) {
        this.returnObject = returnObject;
    }

    @Override
    public Object getReturnObject() {
        return returnObject;
    }

    void setThis(Object target) {
        this.target = target;
    }

    @Override
    public Object getThis() {
        return target;
    }

    //

    public boolean isComprador() {
        final Usuario usuario = ((UserDetailsImpl) this.getPrincipal()).getUsuario();
        return usuario.getPerfil() == Perfil.COMPRADOR;
    }

    public boolean isVendedor() {
        final Usuario usuario = ((UserDetailsImpl) this.getPrincipal()).getUsuario();
        return usuario.getPerfil() == Perfil.VENDEDOR;
    }
}

谢谢!

祝好,
Carlos Oliveira


<details>
<summary>英文:</summary>
I&#39;m studying tutorial how to create custom security expression and I created threes classes but I got error, I tried google everything, may be I am not updated or some. Can you explain what&#39;s going on?
Error:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: Failed to evaluate expression &#39;isComprador()&#39;] with root cause
Method call: Method isComprador() cannot be found on type org.springframework.security.access.expression.method.MethodSecurityExpressionRoot

MethodSecurityConfig:
```java
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new CustomMethodSecurityExpressionHandler();
}
}

CustomMethodSecurityExpressionHandler:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
        CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(this.trustResolver);
        root.setRoleHierarchy(getRoleHierarchy());
        return root;
    }
}

CustomMethodSecurityExpressionRoot:

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    private Object filterObject;
    private Object returnObject;
    private Object target;

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

    @Override
    public void setFilterObject(Object filterObject) {
        this.filterObject = filterObject;
    }

    @Override
    public Object getFilterObject() {
        return filterObject;
    }

    @Override
    public void setReturnObject(Object returnObject) {
        this.returnObject = returnObject;
    }

    @Override
    public Object getReturnObject() {
        return returnObject;
    }

    void setThis(Object target) {
        this.target = target;
    }

    @Override
    public Object getThis() {
        return target;
    }

    //

    public boolean isComprador() {
        final Usuario usuario = ((UserDetailsImpl) this.getPrincipal()).getUsuario();
        return usuario.getPerfil() == Perfil.COMPRADOR;
    }

    public boolean isVendedor() {
        final Usuario usuario = ((UserDetailsImpl) this.getPrincipal()).getUsuario();
        return usuario.getPerfil() == Perfil.VENDEDOR;
    }
}

Thanks!

Att,
Carlos Oliveira

答案1

得分: 4

我真的强烈推荐使用自定义的 Bean,而不是尝试集成到表达式根中。这样做更容易配置,将您的代码与 Spring Security 解耦,您只需要创建一个简单的 POJO,这使得您的代码更加专注。

使用这种方法,首先创建一个 Spring Bean:

@Component
public class Authz {

    public boolean isComprador() {
        // Authentication 是当前登录的用户
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication != null && "comprador".equals(authentication.getName());
    }
}

然后您可以使用 @beanName.methodName 来引用 Bean 中的方法。在我们的例子中,Bean 的名称是 authz,方法名是 isComprador,所以以下内容有效:

@Service
public class MessageService {
    // 我们将 name 参数传递给我们的自定义表达式 Authz.isComprador
    @PreAuthorize("@authz.isComprador()")
    String greetForName(String name) {
        return "Hello " + name;
    }
}

最后,像正常情况下一样启用方法安全性:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
}

然后,您可以编写一些单元测试来验证其是否正常工作:

@SpringBootTest
class DemoApplicationTests {
    @Autowired
    MessageService service;

    @Test
    @WithMockUser // 以默认用户名 user 运行测试
    void secureWhenForbidden() {
        assertThatCode(() -> service.greetForName("Rob")).isInstanceOf(AccessDeniedException.class);
    }

    @Test
    @WithMockUser("comprador") // 以用户名 comprador 运行测试
    void secureWhenGranted() {
        assertThatCode(() -> service.greetForName("Rob")).doesNotThrowAnyException();
    }
}

您可以在 https://github.com/rwinch/spring-security-sample/tree/method-security-bean-expression 找到一个完整的示例。

英文:

I'd really recommend using a custom bean rather than trying to integrate into the expression root. This is much easier to configure, decouples your code from Spring Security you just create a simple pojo, and allows your code to be more focused.

To use this approach start by creating a Spring Bean:

@Component
public class Authz {
public boolean isComprador() {
// Authentication is the currently logged in user
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null &amp;&amp; &quot;comprador&quot;.equals(authentication.getName());
}
}

Then you can refer to methods in the Bean using @beanName.methodName. In our case, the Bean name is authz and our method is isComprador so the following would work:

@Service
public class MessageService {
// we pass in the name argument into our custom expression Authz.isComprador
@PreAuthorize(&quot;@authz.isComprador()&quot;)
String greetForName(String name) {
return &quot;Hello &quot; + name;
}
}

Finally we just enable method security like normal:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
}

You can then write a few unit tests to prove that it works:

@SpringBootTest
class DemoApplicationTests {
@Autowired
MessageService service;
@Test
@WithMockUser // run the test as a user with the default username of user
void secureWhenForbidden() {
assertThatCode(() -&gt; service.greetForName(&quot;Rob&quot;)).isInstanceOf(AccessDeniedException.class);
}
@Test
@WithMockUser(&quot;comprador&quot;) // run the test as a user with the username of comprador
void secureWhenGranted() {
assertThatCode(() -&gt; service.greetForName(&quot;Rob&quot;)).doesNotThrowAnyException();;
}
}

You can find a complete sample at https://github.com/rwinch/spring-security-sample/tree/method-security-bean-expression

huangapple
  • 本文由 发表于 2020年10月20日 12:29:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/64438498.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定