Spring Security Role Hierarchy 无法使用 Java Config
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29888458/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Security Role Hierarchy not working using Java Config
提问by mingchuno
First of all, I am new to Java Spring Framework. So forgive me if I did not provide enough info. I have tried to add RoleHierarchy into my app but it did not work. Below are the codes I have tried.
首先,我是 Java Spring Framework 的新手。所以如果我没有提供足够的信息,请原谅我。我曾尝试将 RoleHierarchy 添加到我的应用程序中,但没有奏效。以下是我尝试过的代码。
SecurityConfig.java
安全配置文件
// These config is try to set up a user Role Hierarchy
@Bean
public RoleHierarchy roleHierarchy() {
System.out.println("arrive public RoleHierarchy roleHierarchy()");
RoleHierarchyImpl r = new RoleHierarchyImpl();
r.setHierarchy("ROLE_ADMIN > ROLE_STAFF");
r.setHierarchy("ROLE_STAFF > ROLE_USER");
r.setHierarchy("ROLE_DEVELOPER > ROLE_USER");
r.setHierarchy("ROLE_USER > ROLE_GUEST");
return r;
}
@Bean
public AffirmativeBased defaultAccessDecisionManager(RoleHierarchy roleHierarchy){
System.out.println("arrive public AffirmativeBased defaultAccessDecisionManager()");
List<AccessDecisionVoter> decisionVoters = new ArrayList<>();
// webExpressionVoter
WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
DefaultWebSecurityExpressionHandler
expressionHandler = new DefaultWebSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
webExpressionVoter.setExpressionHandler(expressionHandler);
decisionVoters.add(webExpressionVoter);
decisionVoters.add(roleHierarchyVoter(roleHierarchy));
// return new AffirmativeBased(Arrays.asList((AccessDecisionVoter) webExpressionVoter));
return new AffirmativeBased(decisionVoters);
}
@Bean
public RoleHierarchyVoter roleHierarchyVoter(RoleHierarchy roleHierarchy) {
System.out.println("arrive public RoleHierarchyVoter roleHierarchyVoter");
return new RoleHierarchyVoter(roleHierarchy);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// skipping some codes
http
// skipping some codes
.accessDecisionManager(defaultAccessDecisionManager(roleHierarchy()))
// skipping some codes
}
MethodSecurityConfig.java
方法安全配置文件
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Inject
private SecurityConfig securityConfig;
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return securityConfig.authenticationManagerBean();
}
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
System.out.println("arrive protected MethodSecurityExpressionHandler createExpressionHandler()");
DefaultMethodSecurityExpressionHandler d = new DefaultMethodSecurityExpressionHandler();
d.setRoleHierarchy(securityConfig.roleHierarchy());
return d;
}
}
And I have a UserDetailsServiceImpl implements UserDetailsService
that provide the principal
, Authentication
and GrantedAuthority
我有一个UserDetailsServiceImpl implements UserDetailsService
提供principal
,Authentication
和GrantedAuthority
Finally I have some APIs:
最后我有一些 API:
@PreAuthorize("hasRole('ROLE_STAFF')")
@RequestMapping(value = "/api/v1/contactUs", method = RequestMethod.GET)
@PreAuthorize("hasRole('ROLE_DEVELOPER')")
@RequestMapping(value = "/api/v1/system", method = RequestMethod.GET)
The problem is now if I login as ROLE_STAFF, ROLE_DEVELOPER, ROLE_ADMIN, I got the following result.
现在的问题是,如果我以 ROLE_STAFF、ROLE_DEVELOPER、ROLE_ADMIN 身份登录,则会得到以下结果。
| API | ROLE_STAFF | ROLE_DEVELOPER | ROLE_ADMIN |
|-----------|------------|----------------|------------|
| contactUs | 200 | 403 | 403 |
| system | 403 | 200 | 403 |
As you can see ROLE_STAFF
and ROLE_DEVELOPER
work just fine. But I want ROLE_ADMIN
as a super role of both and it didn't work.
正如你所看到的ROLE_STAFF
,ROLE_DEVELOPER
工作得很好。但我想ROLE_ADMIN
同时扮演两者的超级角色,但没有奏效。
FYI, I am using spring-security 3.2.5.RELEASE
仅供参考,我正在使用 spring-security 3.2.5.RELEASE
回答by mingchuno
The issue is in the RoleHierachy, which should be like this:
问题出在 RoleHierachy 中,它应该是这样的:
@Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl r = new RoleHierarchyImpl();
r.setHierarchy("ROLE_ADMIN > ROLE_STAFF and ROLE_ADMIN > ROLE_DEVELOPER and ROLE_STAFF > ROLE_USER and ROLE_DEVELOPER > ROLE_USER");
return r;
}
keep calling setHierarchy()
will override the setting before
继续调用setHierarchy()
将覆盖之前的设置
回答by Lord Nighton
Everytime I want to implement a hierarchy of roles with Spring Security and Java config, I use the following approach:
每次我想使用 Spring Security 和 Java config 实现角色层次结构时,我都会使用以下方法:
We have to add a RoleHierarchyImplbean into context (You see, that I use multiple rolesto build a hierarchy):
@Bean public RoleHierarchyImpl roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_DBA ROLE_DBA > ROLE_USER "); return roleHierarchy; }
Then we need to create web expression handler to pass obtained hierarchy to it:
private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() { DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy()); return defaultWebSecurityExpressionHandler; }
The final step is to add expressionHandler into http.authorizeRequests():
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .expressionHandler(webExpressionHandler()) .antMatchers("/admin/**").access("(hasRole('ROLE_ADMIN') or hasRole('ROLE_DBA')) and isFullyAuthenticated()") .antMatchers("/dba").access("hasRole('ROLE_DBA') and isFullyAuthenticated()") .antMatchers("/dba/**").access("hasRole('ROLE_USER')") .and() .requiresChannel() .antMatchers("/security/**").requiresSecure() .anyRequest().requiresInsecure() .and() .formLogin() .loginPage("/login") .failureUrl("/login?auth=fail") .usernameParameter("username") .passwordParameter("password") .defaultSuccessUrl("/admin") .permitAll() .and() .logout() .logoutUrl("/logout") .deleteCookies("remember-me") .invalidateHttpSession(true) .logoutSuccessUrl("/index") .permitAll() .and() .csrf() .and() .rememberMe().tokenValiditySeconds(1209600) .and() .exceptionHandling().accessDeniedPage("/403") .and() .anonymous().disable() .addFilter(switchUserFilter()); }
我们必须在上下文中添加一个RoleHierarchyImplbean(你看,我使用多个角色来构建层次结构):
@Bean public RoleHierarchyImpl roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_DBA ROLE_DBA > ROLE_USER "); return roleHierarchy; }
然后我们需要创建 web 表达式处理程序将获得的层次结构传递给它:
private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() { DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy()); return defaultWebSecurityExpressionHandler; }
最后一步是将 expressionHandler 添加到 http.authorizeRequests() 中:
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .expressionHandler(webExpressionHandler()) .antMatchers("/admin/**").access("(hasRole('ROLE_ADMIN') or hasRole('ROLE_DBA')) and isFullyAuthenticated()") .antMatchers("/dba").access("hasRole('ROLE_DBA') and isFullyAuthenticated()") .antMatchers("/dba/**").access("hasRole('ROLE_USER')") .and() .requiresChannel() .antMatchers("/security/**").requiresSecure() .anyRequest().requiresInsecure() .and() .formLogin() .loginPage("/login") .failureUrl("/login?auth=fail") .usernameParameter("username") .passwordParameter("password") .defaultSuccessUrl("/admin") .permitAll() .and() .logout() .logoutUrl("/logout") .deleteCookies("remember-me") .invalidateHttpSession(true) .logoutSuccessUrl("/index") .permitAll() .and() .csrf() .and() .rememberMe().tokenValiditySeconds(1209600) .and() .exceptionHandling().accessDeniedPage("/403") .and() .anonymous().disable() .addFilter(switchUserFilter()); }
Result:in this particular example we try to visit /dbasection after we have logged in using admin user (ROLE_ADMIN). Before we created a hierarchy, we had an access denied result, but now we can visit this section without any problems.
结果:在此特定示例中,我们尝试在使用管理员用户 (ROLE_ADMIN) 登录后访问/dba部分。在我们创建层次结构之前,我们有一个访问被拒绝的结果,但现在我们可以毫无问题地访问此部分。
回答by David Rz Ayala
Override the createExpressionHandler
method so it returns a configured global expression handler
覆盖该createExpressionHandler
方法,使其返回配置的全局表达式处理程序
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Autowired
private RoleHierarchy roleHierarchy;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler(){
return methodSecurityExpressionHandler();
}
private DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(){
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
return expressionHandler;
}
@Bean
public RoleHierarchyImpl roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_OWNER > ROLE_USER");
return roleHierarchy;
}
@Bean
public RoleHierarchyVoter roleVoter() {
return new RoleHierarchyVoter(roleHierarchy);
}
@Configuration
public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {}
}
}
回答by Vitaliy Polchuk
For me the solution was having proper bean name for the instance of DefaultWebSecurityExpressionHandler
. The name should be webSecurityExpressionHandler
.
对我来说,解决方案是为DefaultWebSecurityExpressionHandler
. 名字应该是webSecurityExpressionHandler
.
@Bean
public RoleHierarchyImpl roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(Roles.getRoleHierarchy());
return roleHierarchy;
}
@Bean
public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy());
return expressionHandler;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.expressionHandler(webSecurityExpressionHandler())
...
}
回答by user2210419
I don't use Spring RoleHierarchy - because it doea not work for me. But Ussualy I do like this: Define Role interface
我不使用 Spring RoleHierarchy - 因为它对我不起作用。但通常我喜欢这样:定义角色界面
public static interface Role {
String getName();
List<String> getHierarchy();
}
List of my roles (to store in DB):
我的角色列表(存储在数据库中):
public interface AuthStates {
// Spring security works fine only with ROLE_*** prefix
String ANONYMOUS = "ROLE_ANONYMOUS";
String AUTHENTICATED = "ROLE_AUTHENTICATED";
String ADMINISTRATOR = "ROLE_ADMINISTRATOR";
}
Define Anonymous role as basic role class:
将匿名角色定义为基本角色类:
public static class Anonymous implements Role {
private final String name;
private final List<String> hierarchy = Lists.newArrayList(ANONYMOUS);
public Anonymous() {
this(ANONYMOUS);
}
protected Anonymous(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public List<String> getHierarchy() {
return hierarchy;
}
protected void addHierarchy(String name) {
hierarchy.add(name);
}
}
Define Authenticated role (common user role):
定义 Authenticated 角色(普通用户角色):
public static class Authenticated extends Anonymous {
public Authenticated() {
this(AUTHENTICATED);
}
protected Authenticated(String name) {
super(name);
addHierarchy(AUTHENTICATED);
}
}
Define Administrator role (at the top of the evolution):
定义管理员角色(在进化的顶部):
public static class Administrator extends Authenticated {
public Administrator() {
this(ADMINISTRATOR);
}
protected Administrator(String name) {
super(name);
addHierarchy(ADMINISTRATOR);
}
}
Optional - static factory class:
可选 - 静态工厂类:
public static Role getRole(String authState) {
switch (authState) {
case ANONYMOUS: return new Anonymous();
case AUTHENTICATED: return new Authenticated();
case ADMINISTRATOR: return new Administrator();
default: throw new IllegalArgumentException("Wrong auth state");
}
}
In my CustomUserDetailsService (which implements UserDetailsService) I use role like this:
在我的 CustomUserDetailsService(实现 UserDetailsService)中,我使用这样的角色:
private Collection<GrantedAuthority> createAuthority(User user) {
final List<GrantedAuthority> authorities = new ArrayList<>();
AuthStates.Role userAuthState = AuthStates.getRole(user.getAuthState());
for (String role : userAuthState.getHierarchy()) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
In controllers:
在控制器中:
@PreAuthorize("hasRole('ROLE_AUTHENTICATED')")
Will allow user logged in as ROLE_AUTHENTICATED and ROLE_ADMINISTRATOR both.
将允许用户以 ROLE_AUTHENTICATED 和 ROLE_ADMINISTRATOR 身份登录。