在 Spring Security (Spring Boot 2.x) 中,我如何为 @Pre/PostAuthorize 和检查角色提供我自己的实现?
In Spring Security (Spring Boot 2.x) how do I provide my own implementation for @Pre/PostAuthorize and checking roles?
我想使用内置的 Spring @PreAuthorize
注释和 hasRole
、hasAnyRole
等,但有 Spring class es 调用我的实现来确定它是否应该是 true/false。我该怎么做?
WebSecurityConfigurerAdapter
中是否有我可以覆盖的配置?
我需要实施 SecurityExpressionRoot
class 吗?如果是这样,我应该在哪里告诉它使用我的?
我尝试覆盖访问决策管理器并添加我自己的选民,但即使它调用了我的方法并且我 return true
(并且它是一个 AffirmativeBased
管理器),它仍然去 SecurityExpressionRoot.hasAnyRole()
然后 returns false.
public class MyDecisionVoter implements AccessDecisionVoter<Object>
{
@Override
public boolean supports(ConfigAttribute attribute)
{
//We want to always be called
return true;
}
@Override
public boolean supports(Class<?> clazz)
{
//We want to always be called
return true;
}
@Override
public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)
{
//For testing purposes
return ACCESS_GRANTED;
}
}
经理
public class MyAffirmativeBasedDecisionManager extends AffirmativeBased
{
public MyAffirmativeBasedDecisionManager(List<AccessDecisionVoter<?>> decisionVoters)
{
super( decisionVoters );
}
@Override
public boolean supports(Class<?> clazz)
{
for ( AccessDecisionVoter<?> voter : this.getDecisionVoters() )
{
if ( voter.supports( clazz ) )
{
return true;
}
}
return false;
}
}
正在配置
@EnableWebSecurity
@EnableGlobalMethodSecurity( prePostEnabled = true )
public class MyConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception
{
//Turn on OAuth
http.authorizeRequests()
.anyRequest()
.authenticated()
.accessDecisionManager( createDecisionManager() );
}
private AccessDecisionManager createDecisionManager()
{
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler( new DefaultMethodSecurityExpressionHandler() );
decisionVoters.add( new MyDecisionVoter() );
decisionVoters.add( new PreInvocationAuthorizationAdviceVoter( expressionAdvice ) );
decisionVoters.add( new RoleVoter() );
decisionVoters.add( new AuthenticatedVoter() );
return new MyAffirmativeBasedDecisionManager( decisionVoters );
}
}
这应该让他们进入,但失败并显示 403:
@GetMapping( "shouldallow" )
@ResponseBody
@PreAuthorize( "hasRole('ROLE_NOT_EXIST')" )
public String shouldAllow()
{
return "should allow";
}
可能有帮助:
public class AuthorizationService {
public boolean hasAccess(Object obj) {
// your code here
}
}
@GetMapping( "/someurl" )
@PreAuthorize("@authorizationService.hasAccess(#obj)")
public void dummyMethod(@PathVariable("obj") Object obj) {
}
如果你根据某个对象定义权限,你可以直接将它传递给方法。否则你可以忽略 hasAccess
方法中的参数。你应该提供 bean
of AuthorizationService
.
如果你想让 @PreAuthorize("hasRole('USER')")
调用你的方法而不是 SecurityExpressionRoot
中的方法,那么,是的,你需要通过将你自己的 MethodSecurityExpressionHandler
公开为一个豆子。您将覆盖其 createSecurityExpressionRoot
方法:
class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {
@Override
protected MethodSecurityExpressionOperations
createSecurityExpressionRoot(Authentication a, MethodInvocation mi) {
return new MyRoot(super.createSecurityExpressionRoot(a, mi));
}
}
@EnableGlobalMethodSecurity(prePostEnabled=true)
class UsingCustomExpressionHandler extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new MyExpressionHandler();
}
}
但是,您可以先尝试侵入性较小的东西。
使用 Bean
例如,您可以refer to any of your own beans inside a SpEL。因此,如果您创建了一个可以执行计算的 @Bean
,那么您就不需要调用 hasRole
。相反,您可以这样做:
@PreAuthorize("@myBean.evaluate(authentication)")
它为您提供了很大的灵活性,可以根据 Authentication
确定访问权限来做任何您需要做的事情。
制图机构
或者,您可以考虑将自定义角色中的任何内容映射到一组 GrantedAuthority
中。 Spring 安全性中的一些身份验证机制带有映射自定义权限表示的方法。
例如,我注意到您的评论 // turn on OAuth
。如果您因为 OAuth 范围而想要覆盖 hasRole
,您可以使用 oauth2ResourceServer()
和 supply a custom JwtAuthenticationConverter
将您的自定义权限调整为 GrantedAuthority
。在这种情况下,可能根本不需要覆盖 hasRole
。 (当然,我不知道你具体是怎么认证用户的,这只是众多GrantedAuthority
转换中的一个例子。)
我想使用内置的 Spring @PreAuthorize
注释和 hasRole
、hasAnyRole
等,但有 Spring class es 调用我的实现来确定它是否应该是 true/false。我该怎么做?
WebSecurityConfigurerAdapter
中是否有我可以覆盖的配置?
我需要实施 SecurityExpressionRoot
class 吗?如果是这样,我应该在哪里告诉它使用我的?
我尝试覆盖访问决策管理器并添加我自己的选民,但即使它调用了我的方法并且我 return true
(并且它是一个 AffirmativeBased
管理器),它仍然去 SecurityExpressionRoot.hasAnyRole()
然后 returns false.
public class MyDecisionVoter implements AccessDecisionVoter<Object>
{
@Override
public boolean supports(ConfigAttribute attribute)
{
//We want to always be called
return true;
}
@Override
public boolean supports(Class<?> clazz)
{
//We want to always be called
return true;
}
@Override
public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)
{
//For testing purposes
return ACCESS_GRANTED;
}
}
经理
public class MyAffirmativeBasedDecisionManager extends AffirmativeBased
{
public MyAffirmativeBasedDecisionManager(List<AccessDecisionVoter<?>> decisionVoters)
{
super( decisionVoters );
}
@Override
public boolean supports(Class<?> clazz)
{
for ( AccessDecisionVoter<?> voter : this.getDecisionVoters() )
{
if ( voter.supports( clazz ) )
{
return true;
}
}
return false;
}
}
正在配置
@EnableWebSecurity
@EnableGlobalMethodSecurity( prePostEnabled = true )
public class MyConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception
{
//Turn on OAuth
http.authorizeRequests()
.anyRequest()
.authenticated()
.accessDecisionManager( createDecisionManager() );
}
private AccessDecisionManager createDecisionManager()
{
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler( new DefaultMethodSecurityExpressionHandler() );
decisionVoters.add( new MyDecisionVoter() );
decisionVoters.add( new PreInvocationAuthorizationAdviceVoter( expressionAdvice ) );
decisionVoters.add( new RoleVoter() );
decisionVoters.add( new AuthenticatedVoter() );
return new MyAffirmativeBasedDecisionManager( decisionVoters );
}
}
这应该让他们进入,但失败并显示 403:
@GetMapping( "shouldallow" )
@ResponseBody
@PreAuthorize( "hasRole('ROLE_NOT_EXIST')" )
public String shouldAllow()
{
return "should allow";
}
可能有帮助:
public class AuthorizationService {
public boolean hasAccess(Object obj) {
// your code here
}
}
@GetMapping( "/someurl" )
@PreAuthorize("@authorizationService.hasAccess(#obj)")
public void dummyMethod(@PathVariable("obj") Object obj) {
}
如果你根据某个对象定义权限,你可以直接将它传递给方法。否则你可以忽略 hasAccess
方法中的参数。你应该提供 bean
of AuthorizationService
.
如果你想让 @PreAuthorize("hasRole('USER')")
调用你的方法而不是 SecurityExpressionRoot
中的方法,那么,是的,你需要通过将你自己的 MethodSecurityExpressionHandler
公开为一个豆子。您将覆盖其 createSecurityExpressionRoot
方法:
class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {
@Override
protected MethodSecurityExpressionOperations
createSecurityExpressionRoot(Authentication a, MethodInvocation mi) {
return new MyRoot(super.createSecurityExpressionRoot(a, mi));
}
}
@EnableGlobalMethodSecurity(prePostEnabled=true)
class UsingCustomExpressionHandler extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new MyExpressionHandler();
}
}
但是,您可以先尝试侵入性较小的东西。
使用 Bean
例如,您可以refer to any of your own beans inside a SpEL。因此,如果您创建了一个可以执行计算的 @Bean
,那么您就不需要调用 hasRole
。相反,您可以这样做:
@PreAuthorize("@myBean.evaluate(authentication)")
它为您提供了很大的灵活性,可以根据 Authentication
确定访问权限来做任何您需要做的事情。
制图机构
或者,您可以考虑将自定义角色中的任何内容映射到一组 GrantedAuthority
中。 Spring 安全性中的一些身份验证机制带有映射自定义权限表示的方法。
例如,我注意到您的评论 // turn on OAuth
。如果您因为 OAuth 范围而想要覆盖 hasRole
,您可以使用 oauth2ResourceServer()
和 supply a custom JwtAuthenticationConverter
将您的自定义权限调整为 GrantedAuthority
。在这种情况下,可能根本不需要覆盖 hasRole
。 (当然,我不知道你具体是怎么认证用户的,这只是众多GrantedAuthority
转换中的一个例子。)