Java Flux contains_spring - Spring Webflux安全中的角色层次结构

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

ReactiveUserDetailsS​​ervice

ReactiveAuthenticationManager

ServerSecurityContextRepository

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

我有一个角色为USER的用户,但他在命中带有GUEST角色的 Controller 时被拒绝403。角色层次结构为:“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 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 save(ServerWebExchange swe, SecurityContext sc) {

throw new UnsupportedOperationException("Not supported yet.");

}

@Override

public Mono 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 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方法,以便从您的http对象(access(...))调用ServerHttpSecurity。即:

public class CustomReactiveAuthorizationManager implements ReactiveAuthorizationManager {

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 check(Mono 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();

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值