Spring Security中始终拒绝访问-DenyAllPermissionEvaluator [英] Access is Always Denied in Spring Security - DenyAllPermissionEvaluator
问题描述
我已经在Spring Boot应用程序中配置了ACL。 ACL配置如下:
@Configuration
@ComponentScan(basePackages = com.company)
@EnableGlobalMethodSecurity(prePostEnabled = true,secureEnabled = true)
公共类ACLConfigration扩展了GlobalMethodSecurityConfiguration {
@Autowired
DataSource dataSource;
@Bean
public EhCacheBasedAclCache aclCache(){
返回新的EhCacheBasedAclCache(aclEhCacheFactoryBean()。getObject(),permissionGrantingStrategy(),aclAuthorizationStrategy());
}
@Bean
public EhCacheFactoryBean aclEhCacheFactoryBean(){
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
ehCacheFactoryBean.setCacheManager(aclCacheManager()。getObject());
ehCacheFactoryBean.setCacheName( aclCache);
返回ehCacheFactoryBean;
}
@Bean
public EhCacheManagerFactoryBean aclCacheManager(){
return new EhCacheManagerFactoryBean();
}
@Bean
public DefaultPermissionGrantingStrategyPermissionGrantingStrategy(){
ConsoleAuditLogger consoleAuditLogger = new ConsoleAuditLogger();
返回新的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(){
返回新的JdbcMutableAclService(dataSource,lookupStrategy(),aclCache());
}
@Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler(){
返回新的DefaultMethodSecurityExpressionHandler();
}
@Override
公共MethodSecurityExpressionHandler createExpressionHandler(){
DefaultMethodSecurityExpressionHandler expressionHandler = defaultMethodSecurityExpressionHandler();
expression Handler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
return expressionHandler;
}
}
参考文献:
,安全配置如下:
@Configuration
@EnableWebSecurity
公共类CustomSecurityConfiguration扩展了WebSecurityConfigurerAdapter {
@Bean
public AuthenticationEntryPoint entryPoint(){
返回新的LoginUrlAuthenticationEntryPoint( /认证);
}
@Override
protected void configure(HttpSecurity http)引发异常{
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)引发异常{
web.ignoring()。antMatchers(HttpMethod.OPTIONS, / ** );
}
@Bean
public CustomUsernamePasswordAuthenticationFilter authenticationFilter()
引发异常{
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
类:
@Component
公共类CustomAuthenticationProvider实现AuthenticationProvider {
@Autowired
private UsersService usersService;
@Override
public Authentication authenticate(Authentication authentication)
抛出AuthenticationException {
字符串用户名= authentication.getName();
字符串密码= authentication.getCredentials()。toString();
用户user = usersService.findOne(用户名);
if(user!= null& usersService.comparePassword(user,password)){
返回新的UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
AuthorityUtils.commaSeparatedStringToAuthorityList(
user.getUserRoles()。stream()。collect(Collectors.joining(,)))));
} else {
返回null;
}
}
@Override
public boolean support(Class<?> authentication){
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
这是我的 CustomUsernamePasswordAuthenticationToken
:
公共类CustomUsernamePasswordAuthenticationFilter扩展了UsernamePasswordAuthenticationFilter {
@Override
公共身份验证尝试身份验证(HttpServletRequest请求,HttpServletResponse响应)
引发AuthenticationException {
if(!request.getMethod()。equals( POST))
引发新的AuthenticationServiceException(String .format(不支持身份验证方法:%s,request.getMethod()));
试试{
CustomUsernamePasswordAuthenticationForm form = new ObjectMapper()。readValue(request.getReader(),CustomUsernamePasswordAuthenticationForm.class);
字符串用户名= form.getUsername();
字符串密码= form.getPassword();
if(username == null)
username =;
if(password == null)
password =;
UsernamePasswordAuthenticationToken令牌=新的UsernamePasswordAuthenticationToken(用户名,密码);
setDetails(请求,令牌);
返回getAuthenticationManager()。authenticate(token);
} catch(IOException异常){
抛出新的CustomAuthenticationException(异常);
}
}
私有类CustomAuthenticationException扩展了RuntimeException {
private CustomAuthenticationException(Throwable throwable){
super(throwable);
}
}
}
除上述内容外,我有 CustomAuthenticationFailureHandler
, CustomAuthenticationSuccessHandler
, CustomNoRedirectStrategy
和 CustomUsernamePasswordAuthenticationForm
我为了这个问题的长度而略过了。
我正在使用可以在此处。
我正在向与acl相关的表中添加条目,如下所示:
插入acl_class值(1,com.company.project.domain.users.User)
插入acl_sid值(1、1, demo)
(我有一个用户名为 demo
的用户)
插入acl_object_identity值(1,1,1,NULL,1,0)
插入acl_entr y值(1、1、1、1、1、1、1、1)
但是所有我得到的是:
拒绝对对象com.company.project.domain.users.User @的用户演示权限 READ 4a49e9b4
在我的
@PostFilter( hasPermission(filterObject,'READ'))
I我怀疑这里有几个问题:
-
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,必填=假,defaultValue = 10)整数限制,
@Min(0)@Min(0)@RequestParam(value = page,必填= false ,defaultValue = 0)整数页,
@RequestParam(value = email,required = false)字符串电子邮件,
@RequestParam(value = firstName,required = false)字符串firstName,
@RequestParam(value = lastName,必填=假)字符串lastName,
@RequestParam(value = userRole,必填=假)字符串userRole){
返回usersService.find(
限制,
页,
电子邮件,
firstName,
lastName,
userRole);
}
更新#2:
该问题现在反映了有关身份验证/授权/ ACL的所有设置。
更新#3:
我现在已经很接近要解决此问题了,剩下的唯一解决方法是:
如果有人可以帮助我解决这个问题,我终于可以写信了
这是期待已久的答案:
文档明确描述:
要使用hasPermission()表达式,必须在应用程序上下文中显式配置
PermissionEvaluator。看起来
是这样的:
所以基本上我是在我的 AclConfiguration中做的
扩展了 GlobalMethodSecurityConfiguration
:
@Override
受保护的MethodSecurityExpressionHandler createExpressionHandler(){
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expression Handler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
return expressionHandler;
}
Spring尚未对此进行处理!
我不得不将 AclConfig
和 GlobalMethodSecurityConfiguration
分开。当在后者中定义了 @Bean
时,上述方法未得到处理,这可能是一个错误(如果没有,欢迎您提供有关主题的任何说明)。 / p>
I have configured ACL in my Spring Boot application. The ACL configuration is as follows:
@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;
}
}
References:
and the security configuration is as follows:
@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();
}
}
My 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);
}
}
Here's my 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);
}
}
}
Apart from the above, I have CustomAuthenticationFailureHandler
, CustomAuthenticationSuccessHandler
, CustomNoRedirectStrategy
and CustomUsernamePasswordAuthenticationForm
which I skipped for the sake of this question's length.
And I am using MySQL schema that can be found here.
I am adding entries to my acl related tables as follows:
INSERT INTO acl_class VALUES (1, com.company.project.domain.users.User)
INSERT INTO acl_sid VALUES (1, 1, "demo")
(I have a user with username 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)
But all I am getting is:
Denying user demo permission 'READ' on object com.company.project.domain.users.User@4a49e9b4
in my
@PostFilter("hasPermission(filterObject, 'READ')")
I am suspecting of several issues here:
- The
hasPermission
expression: I have substituted it with 'READ' and '1', but to no extent. - My database entries are not right
- I am not implementing a custom permission evaluator. Is this required, or is
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
enough?
Update
Sample method where @PostFilter
is used:
@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);
}
Update #2:
The question now reflects everything set up in regards to authentication/authorization/ACL.
Update #3:
I am now very close to resolve the issue, the only thing left is to resolve this:
If anyone could help me with that question, I can finally have a write up of what I have went through to resolve this.
Here's the long waited answer:
The documentation clearly describes:
To use hasPermission() expressions, you have to explicitly configure a PermissionEvaluator in your application context. This would look something like this:
so basically I was doing in my AclConfiguration
which extends GlobalMethodSecurityConfiguration
:
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
return expressionHandler;
}
Which was not getting processed by Spring!
I had to separate AclConfig
and GlobalMethodSecurityConfiguration
. When there are @Bean
s defined in the latter, the above method is not getting processed, which might be a bug (if not, any clarification on subject is welcome).
这篇关于Spring Security中始终拒绝访问-DenyAllPermissionEvaluator的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!