gpt4 book ai didi

spring - Spring Webflux 安全中的角色层次结构

转载 作者:行者123 更新时间:2023-12-02 19:30:19 24 4
gpt4 key购买 nike

我通过实现以下方式实现了 Webflux 安全性:

  • ReactiveUserDetailsS​​ervice
  • ReactiveAuthenticationManager
  • 服务器安全上下文存储库

现在,我尝试按照此处的文档介绍 RoleHierarchy:Role Hierarchy Docs

我有一个具有 USER 角色的用户,但他在点击带有 GUEST 角色注释的 Controller 时收到 403 Denied。角色层次结构为:“ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST”

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {

private final DaoAuthenticationManager reactiveAuthenticationManager;

private final SecurityContextRepository securityContextRepository;

private static final String ROLE_HIERARCHIES = "ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST";

@Autowired
public SecurityConfig(DaoAuthenticationManager reactiveAuthenticationManager,
SecurityContextRepository securityContextRepository) {
this.reactiveAuthenticationManager = reactiveAuthenticationManager;
this.securityContextRepository = securityContextRepository;
}

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}

@Bean(name = "roleHierarchy")
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(ROLE_HIERARCHIES);
return roleHierarchy;
}

@Bean(name = "roleVoter")
public RoleVoter roleVoter() {
return new RoleHierarchyVoter(roleHierarchy());
}
}
<小时/>
@Component
public class DaoAuthenticationManager implements ReactiveAuthenticationManager {

private final DaoUserDetailsService userDetailsService;

private final Scheduler scheduler;

@Autowired
public DaoAuthenticationManager(DaoUserDetailsService userDetailsService,
Scheduler scheduler) {
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
this.userDetailsService = userDetailsService;
this.scheduler = scheduler;
}

@Override
public Mono<Authentication> authenticate(Authentication authentication) {
final String username = authentication.getName();
return this.userDetailsService.findByUsername(username)
.publishOn(this.scheduler)
.switchIfEmpty(
Mono.defer(() -> Mono.error(new UsernameNotFoundException("Invalid Username"))))
.map(u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(),
u.getAuthorities()));
}
}
<小时/>
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {

private final DaoAuthenticationManager authenticationManager;

@Autowired
public SecurityContextRepository(DaoAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}

@Override
public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
ServerHttpRequest request = swe.getRequest();
if (request.getHeaders().containsKey("userName") &&
!Objects.requireNonNull(request.getHeaders().get("userName")).isEmpty()) {
String userName = Objects.requireNonNull(swe
.getRequest()
.getHeaders()
.get("userName")).get(0);

Authentication auth = new UsernamePasswordAuthenticationToken(userName,
Security.PASSWORD);
return this.authenticationManager.authenticate(auth).map(SecurityContextImpl::new);
} else {
return Mono.empty();
}
}


}

无论如何,要让角色层次结构在 Webflux 安全性中发挥作用。

编辑

Controller :

@GetMapping
@PreAuthorize("hasRole('USER')")
public Mono<Device> getDevice(@RequestParam String uuid) {
return deviceService.getDevice(uuid);
}

正常的角色授权对我来说是有效的,不起作用的是层次结构部分。

最佳答案

这是一个非常简单的解决方案,通过重写 DefaultMethodSecurityExpressionHandler 来实现。

我想你用这个表达式之王注释了你的 Controller :@PreAuthorize("hasRole('ROLE_USER')")

securityConfig.java

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {

private final DaoAuthenticationManager reactiveAuthenticationManager;

private final SecurityContextRepository securityContextRepository;

private static final String ROLE_HIERARCHY = "ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST";

@Autowired
public SecurityConfig(DaoAuthenticationManager reactiveAuthenticationManager,
SecurityContextRepository securityContextRepository) {
this.reactiveAuthenticationManager = reactiveAuthenticationManager;
this.securityContextRepository = securityContextRepository;
}

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}

@Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(ROLE_HIERARCHY);
return roleHierarchy;
}

// Overriding spring default bean
@Bean
public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setRoleHierarchy(roleHierarchy);
return handler;
}

}

然后,您必须通过修改应用程序属性文件来授权 spring bean 覆盖:

application.properties

spring.main.allow-bean-definition-overriding=true

来源:issue 1 issue role hierarchy doc

<小时/>

再进一步...这部分可以优化和更清晰。

使用 ServerHttpSecurity 对象中的 url 模式设置。

请注意,以下设置不会使用角色层次结构:

  @Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers("/user/**").hasRole("ROLE_USER") // This won't use role hierarchy because it will use implemention of hasRole defined in your 'reactiveAuthenticationManager'
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}

解决方案可能是创建您自己的 ReactiveAuthorizationManager 实现并覆盖 check 方法,以便从以下位置调用 access(...)您的 http 对象 (ServerHttpSecurity)。即:

public class CustomReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {

private final static Logger logger = LoggerFactory.getLogger(CustomReactiveAuthorizationManager.class);

private final RoleHierarchyVoter roleHierarchyVoter;

private final String authority;

CustomReactiveAuthorizationManager(String role, RoleHierarchy roleHierarchy) {
this.authority = ROLE_PREFIX + role;
this.roleHierarchyVoter = new RoleHierarchyVoter(roleHierarchy);
}

@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {

return authentication
.map(a -> {
ConfigAttribute ca = (ConfigAttribute) () -> authority;
int voteResult = roleHierarchyVoter.vote(a, object, Collections.singletonList(ca));
boolean isAuthorized = voteResult == AccessDecisionVoter.ACCESS_GRANTED;
return new AuthorizationDecision(isAuthorized);
})
.defaultIfEmpty(new AuthorizationDecision(false))
.doOnError(error -> logger.error("An error occured voting decision", error));
}

}

然后调用访问方法:

    @Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, RoleHierarchy roleHierarchy() {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers("/user/**").access(new CustomReactiveAuthorizationManager<>("USER", roleHierarchy))
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}

关于spring - Spring Webflux 安全中的角色层次结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54399224/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com