在 Spring 安全性中始终拒绝访问 - DenyAllPermissionEvaluator
Access is Always Denied in Spring Security - DenyAllPermissionEvaluator
我已经在 Spring 引导应用程序中配置了 ACL。 ACL配置如下:
@Configuration
@ComponentScan(basePackages = "com.company")
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ACLConfigration extends GlobalMethodSecurityConfiguration {
@Autowired
DataSource dataSource;
@Bean
public EhCacheBasedAclCache aclCache() {
return new EhCacheBasedAclCache(aclEhCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy());
}
@Bean
public EhCacheFactoryBean aclEhCacheFactoryBean() {
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
ehCacheFactoryBean.setCacheName("aclCache");
return ehCacheFactoryBean;
}
@Bean
public EhCacheManagerFactoryBean aclCacheManager() {
return new EhCacheManagerFactoryBean();
}
@Bean
public DefaultPermissionGrantingStrategy permissionGrantingStrategy() {
ConsoleAuditLogger consoleAuditLogger = new ConsoleAuditLogger();
return new DefaultPermissionGrantingStrategy(consoleAuditLogger);
}
@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ACL_ADMIN"));
}
@Bean
public LookupStrategy lookupStrategy() {
return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
}
@Bean
public JdbcMutableAclService aclService() {
return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
}
@Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
return new DefaultMethodSecurityExpressionHandler();
}
@Override
public MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = defaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
return expressionHandler;
}
}
参考文献:
- SO Q2
安全配置如下:
@Configuration
@EnableWebSecurity
public class CustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationEntryPoint entryPoint() {
return new LoginUrlAuthenticationEntryPoint("/authenticate");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/authenticate/**").permitAll()
.anyRequest().fullyAuthenticated()
.and().requestCache().requestCache(new NullRequestCache())
.and().addFilterBefore(authenticationFilter(), CustomUsernamePasswordAuthenticationFilter.class);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
@Bean
public CustomUsernamePasswordAuthenticationFilter authenticationFilter()
throws Exception {
CustomUsernamePasswordAuthenticationFilter authenticationFilter = new CustomUsernamePasswordAuthenticationFilter();
authenticationFilter.setUsernameParameter("username");
authenticationFilter.setPasswordParameter("password");
authenticationFilter.setFilterProcessesUrl("/authenticate");
authenticationFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
authenticationFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
authenticationFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
我的CustomAuthenticationProvider
class:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UsersService usersService;
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
User user = usersService.findOne(username);
if(user != null && usersService.comparePassword(user, password)){
return new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
AuthorityUtils.commaSeparatedStringToAuthorityList(
user.getUserRoles().stream().collect(Collectors.joining(","))));
} else {
return null;
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
这是我的 CustomUsernamePasswordAuthenticationToken
:
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if(!request.getMethod().equals("POST"))
throw new AuthenticationServiceException(String.format("Authentication method not supported: %s", request.getMethod()));
try {
CustomUsernamePasswordAuthenticationForm form = new ObjectMapper().readValue(request.getReader(), CustomUsernamePasswordAuthenticationForm.class);
String username = form.getUsername();
String password = form.getPassword();
if(username == null)
username = "";
if(password == null)
password = "";
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, token);
return getAuthenticationManager().authenticate(token);
} catch (IOException exception) {
throw new CustomAuthenticationException(exception);
}
}
private class CustomAuthenticationException extends RuntimeException {
private CustomAuthenticationException(Throwable throwable) {
super(throwable);
}
}
}
除此之外,还有CustomAuthenticationFailureHandler
、CustomAuthenticationSuccessHandler
、CustomNoRedirectStrategy
和CustomUsernamePasswordAuthenticationForm
,为了这个问题的篇幅,我跳过了
我正在使用 MySQL 可以找到的架构 here。
我正在将条目添加到我的 acl 相关表中,如下所示:
INSERT INTO acl_class VALUES (1, com.company.project.domain.users.User)
INSERT INTO acl_sid VALUES (1, 1, "demo")
(我有一个用户名为 demo
)
INSERT INTO acl_object_identity VALUES (1, 1, 1, NULL, 1, 0)
INSERT INTO acl_entry VALUES (1, 1, 1, 1, 1, 1, 1, 1)
但我得到的只是:
Denying user demo permission 'READ' on object com.company.project.domain.users.User@4a49e9b4
在我的
@PostFilter("hasPermission(filterObject, 'READ')")
我怀疑这里有几个问题:
hasPermission
表达式:我已将其替换为 'READ' 和“1”,但没有任何程度。
- 我的数据库条目不正确
- 我没有实施自定义权限评估器。这是必需的,还是
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
就足够了?
更新
使用@PostFilter
的示例方法:
@RequestMapping(method = RequestMethod.GET)
@PostFilter("hasPermission(filterObject, 'READ')")
List<User> find(@Min(0) @RequestParam(value = "limit", required = false, defaultValue = "10") Integer limit,
@Min(0) @RequestParam(value = "page", required = false, defaultValue = "0") Integer page,
@RequestParam(value = "email", required = false) String email,
@RequestParam(value = "firstName", required = false) String firstName,
@RequestParam(value = "lastName", required = false) String lastName,
@RequestParam(value = "userRole", required = false) String userRole) {
return usersService.find(
limit,
page,
email,
firstName,
lastName,
userRole);
}
更新#2:
问题现在反映了关于 authentication/authorization/ACL 的所有设置。
更新#3:
我现在非常接近解决这个问题,唯一剩下的就是解决这个问题:
如果有人能帮我解决这个问题,我终于可以写下我为解决这个问题所经历的一切。
我升级了我的应用程序以使用 Spring Security 4.2。1.RELEASE 然后我开始遇到所有 @PreAuthorize
注释方法中的意外访问被拒绝,这在之前工作得很好升级。
我调试了 spring 安全代码,我意识到问题是所有要检查的角色都以默认字符串 "ROLE_" 为前缀,而不管我将默认前缀设置为空的事实,因为如下代码所示。
auth.ldapAuthentication()
.groupSearchBase(ldapProperties.getProperty("groupSearchBase"))
.groupRoleAttribute(ldapProperties.getProperty("groupRoleAttribute"))
.groupSearchFilter(ldapProperties.getProperty("groupSearchFilter"))
//this call used to be plenty to override the default prefix
.rolePrefix("")
.userSearchBase(ldapProperties.getProperty("userSearchBase"))
.userSearchFilter(ldapProperties.getProperty("userSearchFilter"))
.contextSource(this.ldapContextSource);
我所有的控制器方法都用 @PreAuthorize("hasRole('my_ldap_group_name')")
注释,但是,框架没有考虑我的空角色前缀设置,因此它使用 ROLE_my_ldap_group_name 改为检查实际角色。
深入研究框架的代码后,我意识到 class org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler
的默认角色前缀仍然设置为 "ROLE_"
。我跟踪了它的值的来源,我发现它首先检查 class org.springframework.security.config.core.GrantedAuthorityDefaults
的声明 bean 以在 bean org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer
的第一次初始化期间查找默认前缀, 然而,由于这个初始化器 bean 找不到它声明的,它最终使用了前面提到的默认前缀。
我认为这不是预期的行为:Spring 安全性应该考虑来自 ldapAuthentication 的相同 rolePrefix,但是,为了解决这个问题,有必要将 bean org.springframework.security.config.core.GrantedAuthorityDefaults
添加到我的应用上下文(我使用的是基于注解的配置),如下:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CesSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String ROLE_PREFIX = "";
//... ommited code ...
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(ROLE_PREFIX);
}
}
也许你遇到了同样的问题 - 我可以看到你正在使用 DefaultMethodSecurityExpressionHandler 并且它也使用 bean GrantedAuthorityDefaults,所以如果你使用与我相同的 Spring 安全版本 - 4.2 .1.RELEASE 你可能 运行 遇到了同样的问题。
您在数据库中的数据和您的配置看起来不错。我一直使用 @PostFilter("hasPermission(filterObject, 'READ')")
。
我会检查以确保扩展 UserDetails 的用户 class 与数据库中的 getUsername() 用户名相同 return。除了检查以确保您的安全性和应用程序处于同一上下文中。
hasPermission method take an Authentication 对象,因为它是第一个参数。
boolean hasPermission(Authentication authentication,
Object targetDomainObject,
Object permission)
Authentication 对象是 class 的实现,通常是 UsernamePasswordAuthenticationToken。因此 getPrincipal() 方法需要 return 一个具有 getUserName() 方法的对象,该方法 return 与数据库中的对象相同。
public PrincipalSid(Authentication authentication) {
Assert.notNull(authentication, "Authentication required");
Assert.notNull(authentication.getPrincipal(), "Principal required");
if (authentication.getPrincipal() instanceof UserDetails) {
this.principal = ((UserDetails) authentication.getPrincipal()).getUsername();
}
else {
this.principal = authentication.getPrincipal().toString();
}
}
这是期待已久的答案:
documentation 明确描述:
To use hasPermission() expressions, you have to explicitly configure a
PermissionEvaluator in your application context. This would look
something like this:
所以基本上我在我的 AclConfiguration
中做了扩展 GlobalMethodSecurityConfiguration
:
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
return expressionHandler;
}
未被 Spring 处理!
我不得不将 AclConfig
和 GlobalMethodSecurityConfiguration
分开。当后者定义了 @Bean
时,上述方法没有得到处理,这可能是一个错误(如果没有,欢迎对主题进行任何澄清)。
很明显,AclPermissionEvaluator
不被尊重。我的路上出现了同样的问题,通过2个步骤解决了:
- 在重写方法中创建表达式处理程序而不是注入
WebSecurityConfig
是在与 GlobalMethodSecurityConfiguration
相同的 class 中创建的,否则,GlobalMethodSecurityConfiguration
将不起作用,也不会覆盖处理程序。
我已经在 Spring 引导应用程序中配置了 ACL。 ACL配置如下:
@Configuration
@ComponentScan(basePackages = "com.company")
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ACLConfigration extends GlobalMethodSecurityConfiguration {
@Autowired
DataSource dataSource;
@Bean
public EhCacheBasedAclCache aclCache() {
return new EhCacheBasedAclCache(aclEhCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy());
}
@Bean
public EhCacheFactoryBean aclEhCacheFactoryBean() {
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
ehCacheFactoryBean.setCacheName("aclCache");
return ehCacheFactoryBean;
}
@Bean
public EhCacheManagerFactoryBean aclCacheManager() {
return new EhCacheManagerFactoryBean();
}
@Bean
public DefaultPermissionGrantingStrategy permissionGrantingStrategy() {
ConsoleAuditLogger consoleAuditLogger = new ConsoleAuditLogger();
return new DefaultPermissionGrantingStrategy(consoleAuditLogger);
}
@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ACL_ADMIN"));
}
@Bean
public LookupStrategy lookupStrategy() {
return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
}
@Bean
public JdbcMutableAclService aclService() {
return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
}
@Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
return new DefaultMethodSecurityExpressionHandler();
}
@Override
public MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = defaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
return expressionHandler;
}
}
参考文献:
- SO Q2
安全配置如下:
@Configuration
@EnableWebSecurity
public class CustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationEntryPoint entryPoint() {
return new LoginUrlAuthenticationEntryPoint("/authenticate");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/authenticate/**").permitAll()
.anyRequest().fullyAuthenticated()
.and().requestCache().requestCache(new NullRequestCache())
.and().addFilterBefore(authenticationFilter(), CustomUsernamePasswordAuthenticationFilter.class);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
@Bean
public CustomUsernamePasswordAuthenticationFilter authenticationFilter()
throws Exception {
CustomUsernamePasswordAuthenticationFilter authenticationFilter = new CustomUsernamePasswordAuthenticationFilter();
authenticationFilter.setUsernameParameter("username");
authenticationFilter.setPasswordParameter("password");
authenticationFilter.setFilterProcessesUrl("/authenticate");
authenticationFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
authenticationFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
authenticationFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
我的CustomAuthenticationProvider
class:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UsersService usersService;
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
User user = usersService.findOne(username);
if(user != null && usersService.comparePassword(user, password)){
return new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
AuthorityUtils.commaSeparatedStringToAuthorityList(
user.getUserRoles().stream().collect(Collectors.joining(","))));
} else {
return null;
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
这是我的 CustomUsernamePasswordAuthenticationToken
:
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if(!request.getMethod().equals("POST"))
throw new AuthenticationServiceException(String.format("Authentication method not supported: %s", request.getMethod()));
try {
CustomUsernamePasswordAuthenticationForm form = new ObjectMapper().readValue(request.getReader(), CustomUsernamePasswordAuthenticationForm.class);
String username = form.getUsername();
String password = form.getPassword();
if(username == null)
username = "";
if(password == null)
password = "";
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, token);
return getAuthenticationManager().authenticate(token);
} catch (IOException exception) {
throw new CustomAuthenticationException(exception);
}
}
private class CustomAuthenticationException extends RuntimeException {
private CustomAuthenticationException(Throwable throwable) {
super(throwable);
}
}
}
除此之外,还有CustomAuthenticationFailureHandler
、CustomAuthenticationSuccessHandler
、CustomNoRedirectStrategy
和CustomUsernamePasswordAuthenticationForm
,为了这个问题的篇幅,我跳过了
我正在使用 MySQL 可以找到的架构 here。
我正在将条目添加到我的 acl 相关表中,如下所示:
INSERT INTO acl_class VALUES (1, com.company.project.domain.users.User)
INSERT INTO acl_sid VALUES (1, 1, "demo")
(我有一个用户名为 demo
)
INSERT INTO acl_object_identity VALUES (1, 1, 1, NULL, 1, 0)
INSERT INTO acl_entry VALUES (1, 1, 1, 1, 1, 1, 1, 1)
但我得到的只是:
Denying user demo permission 'READ' on object com.company.project.domain.users.User@4a49e9b4
在我的
@PostFilter("hasPermission(filterObject, 'READ')")
我怀疑这里有几个问题:
hasPermission
表达式:我已将其替换为 'READ' 和“1”,但没有任何程度。- 我的数据库条目不正确
- 我没有实施自定义权限评估器。这是必需的,还是
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
就足够了?
更新
使用@PostFilter
的示例方法:
@RequestMapping(method = RequestMethod.GET)
@PostFilter("hasPermission(filterObject, 'READ')")
List<User> find(@Min(0) @RequestParam(value = "limit", required = false, defaultValue = "10") Integer limit,
@Min(0) @RequestParam(value = "page", required = false, defaultValue = "0") Integer page,
@RequestParam(value = "email", required = false) String email,
@RequestParam(value = "firstName", required = false) String firstName,
@RequestParam(value = "lastName", required = false) String lastName,
@RequestParam(value = "userRole", required = false) String userRole) {
return usersService.find(
limit,
page,
email,
firstName,
lastName,
userRole);
}
更新#2:
问题现在反映了关于 authentication/authorization/ACL 的所有设置。
更新#3:
我现在非常接近解决这个问题,唯一剩下的就是解决这个问题:
如果有人能帮我解决这个问题,我终于可以写下我为解决这个问题所经历的一切。
我升级了我的应用程序以使用 Spring Security 4.2。1.RELEASE 然后我开始遇到所有 @PreAuthorize
注释方法中的意外访问被拒绝,这在之前工作得很好升级。
我调试了 spring 安全代码,我意识到问题是所有要检查的角色都以默认字符串 "ROLE_" 为前缀,而不管我将默认前缀设置为空的事实,因为如下代码所示。
auth.ldapAuthentication()
.groupSearchBase(ldapProperties.getProperty("groupSearchBase"))
.groupRoleAttribute(ldapProperties.getProperty("groupRoleAttribute"))
.groupSearchFilter(ldapProperties.getProperty("groupSearchFilter"))
//this call used to be plenty to override the default prefix
.rolePrefix("")
.userSearchBase(ldapProperties.getProperty("userSearchBase"))
.userSearchFilter(ldapProperties.getProperty("userSearchFilter"))
.contextSource(this.ldapContextSource);
我所有的控制器方法都用 @PreAuthorize("hasRole('my_ldap_group_name')")
注释,但是,框架没有考虑我的空角色前缀设置,因此它使用 ROLE_my_ldap_group_name 改为检查实际角色。
深入研究框架的代码后,我意识到 class org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler
的默认角色前缀仍然设置为 "ROLE_"
。我跟踪了它的值的来源,我发现它首先检查 class org.springframework.security.config.core.GrantedAuthorityDefaults
的声明 bean 以在 bean org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer
的第一次初始化期间查找默认前缀, 然而,由于这个初始化器 bean 找不到它声明的,它最终使用了前面提到的默认前缀。
我认为这不是预期的行为:Spring 安全性应该考虑来自 ldapAuthentication 的相同 rolePrefix,但是,为了解决这个问题,有必要将 bean org.springframework.security.config.core.GrantedAuthorityDefaults
添加到我的应用上下文(我使用的是基于注解的配置),如下:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CesSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String ROLE_PREFIX = "";
//... ommited code ...
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(ROLE_PREFIX);
}
}
也许你遇到了同样的问题 - 我可以看到你正在使用 DefaultMethodSecurityExpressionHandler 并且它也使用 bean GrantedAuthorityDefaults,所以如果你使用与我相同的 Spring 安全版本 - 4.2 .1.RELEASE 你可能 运行 遇到了同样的问题。
您在数据库中的数据和您的配置看起来不错。我一直使用 @PostFilter("hasPermission(filterObject, 'READ')")
。
我会检查以确保扩展 UserDetails 的用户 class 与数据库中的 getUsername() 用户名相同 return。除了检查以确保您的安全性和应用程序处于同一上下文中。
hasPermission method take an Authentication 对象,因为它是第一个参数。
boolean hasPermission(Authentication authentication,
Object targetDomainObject,
Object permission)
Authentication 对象是 class 的实现,通常是 UsernamePasswordAuthenticationToken。因此 getPrincipal() 方法需要 return 一个具有 getUserName() 方法的对象,该方法 return 与数据库中的对象相同。
public PrincipalSid(Authentication authentication) {
Assert.notNull(authentication, "Authentication required");
Assert.notNull(authentication.getPrincipal(), "Principal required");
if (authentication.getPrincipal() instanceof UserDetails) {
this.principal = ((UserDetails) authentication.getPrincipal()).getUsername();
}
else {
this.principal = authentication.getPrincipal().toString();
}
}
这是期待已久的答案:
documentation 明确描述:
To use hasPermission() expressions, you have to explicitly configure a PermissionEvaluator in your application context. This would look something like this:
所以基本上我在我的 AclConfiguration
中做了扩展 GlobalMethodSecurityConfiguration
:
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
return expressionHandler;
}
未被 Spring 处理!
我不得不将 AclConfig
和 GlobalMethodSecurityConfiguration
分开。当后者定义了 @Bean
时,上述方法没有得到处理,这可能是一个错误(如果没有,欢迎对主题进行任何澄清)。
很明显,AclPermissionEvaluator
不被尊重。我的路上出现了同样的问题,通过2个步骤解决了:
- 在重写方法中创建表达式处理程序而不是注入
WebSecurityConfig
是在与GlobalMethodSecurityConfiguration
相同的 class 中创建的,否则,GlobalMethodSecurityConfiguration
将不起作用,也不会覆盖处理程序。