Webflux JWT 授权无法正常工作

Webflux JWT Authorization not working fine

我在 spring 反应上下文 (webflux) 中关注关于 JWT 的 tutorial

令牌生成工作正常,但是当我将 Authorizationbearer

一起使用时,授权不起作用

这是我所做的:

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig{

    @Autowired private JWTReactiveAuthenticationManager authenticationManager;

    @Autowired private SecurityContextRepository securityContext;

    @Bean public SecurityWebFilterChain configure(ServerHttpSecurity http){

        return http.exceptionHandling()
        .authenticationEntryPoint((swe , e) -> {
            return Mono.fromRunnable(()->{
                System.out.println( "authenticationEntryPoint user trying to access unauthorized api end points : "+
                                    swe.getRequest().getRemoteAddress()+
                                    " in "+swe.getRequest().getPath());
                swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            });
        }).accessDeniedHandler((swe, e) -> {
            return Mono.fromRunnable(()->{
                System.out.println( "accessDeniedHandler user trying to access unauthorized api end points : "+
                                    swe.getPrincipal().block().getName()+
                                    " in "+swe.getRequest().getPath());
                swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);                    
            });
        })
        .and()
        .csrf().disable()
        .formLogin().disable()
        .httpBasic().disable()
        .authenticationManager(authenticationManager)
        .securityContextRepository(securityContext)
        .authorizeExchange()
        .pathMatchers(HttpMethod.OPTIONS).permitAll()
        .pathMatchers("/auth/login").permitAll()
        .anyExchange().authenticated()
        .and()
        .build();


    }

如您所见,除了登录或基于选项的请求外,我只想拒绝所有未经授权的请求。

登录正常,我正在获取令牌。

但是尝试注销(由于我只是在学习,所以我自己实现了一个调整,使它处于全状态)是行不通的。

这是我的注销控制器:


@RestController
@RequestMapping(AuthController.AUTH)
public class AuthController {

    static final String AUTH = "/auth";

    @Autowired
    private AuthenticationService authService;

    @PostMapping("/login")
    public Mono<ResponseEntity<?>> login(@RequestBody AuthRequestParam arp) {

        String username = arp.getUsername();
        String password = arp.getPassword();

        return authService.authenticate(username, password);
    }

    @PostMapping("/logout")
    public Mono<ResponseEntity<?>> logout(@RequestBody LogoutRequestParam lrp) {

        String token = lrp.getToken();

        return authService.logout(token);
    }

}

注销请求如下:

如上图所示,我相信我做得很好,但是我收到错误日志消息:

authenticationEntryPoint user trying to access unauthorized api end points : /127.0.0.1:45776 in /auth/logout

这是我的安全上下文内容:


/**
 * we use this class to handle the bearer token extraction
 * and pass it to the JWTReactiveAuthentication manager so in the end 
 * we produce
 * 
 * simply said we extract the authorization we authenticate and 
 * depending on our implementation we produce a security context
 */

@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {

    @Autowired
    private JWTReactiveAuthenticationManager authenticationManager;

    @Override
    public Mono<SecurityContext> load(ServerWebExchange swe) {

        ServerHttpRequest request = swe.getRequest();

        String authorizationHeaderContent = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);

        if( authorizationHeaderContent !=null &&  !authorizationHeaderContent.isEmpty() &&  authorizationHeaderContent.startsWith("Bearer ")){

                String token = authorizationHeaderContent.substring(7);

                Authentication authentication = new UsernamePasswordAuthenticationToken(token, token);
                return this.authenticationManager.authenticate(authentication).map((auth) -> {
                    return new SecurityContextImpl(auth);
                });

        }

        return Mono.empty();
    }

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

}

我无法看到或找到我所犯的任何问题或错误。哪里错了?

写法有区别

//Wrong
Jwts.builder()
   .setSubject(username)
   .setClaims(claims)

//Correct
Jwts.builder()
   .setClaims(claims)
   .setSubject(username)

的确,看看DefaultJwtBuilder中的setSubject方法 class :

@Override
public JwtBuilder setSubject(String sub) {
    if (Strings.hasText(sub)) {
        ensureClaims().setSubject(sub);
    } else {
        if (this.claims != null) {
            claims.setSubject(sub);
        }
    }
    return this;
}

当首先调用 setSubject(username) 时,ensureClaims() 会创建一个没有你的 DefaultClaims,如果你调用 setClaims(claims),则先例主题将丢失!这个 JWT 构建器是假的。

否则,您在 JWTReactiveAuthenticationManager 中导入了错误的角色 class,您必须更换:

import org.springframework.context.support.BeanDefinitionDsl.Role;

来自

import com.bridjitlearning.www.jwt.tutorial.domain.Role;

最后且同样重要的是,validateToken() 将 return 总是 false 因为 check(token)put 电话来得太晚了,你必须意识到这一点。您要么删除此检查,要么在调用检查方法之前移动 put 执行。

我不确定你想用 resignTokenMemory 做什么,所以我会让你自己解决:

public Boolean validateToken(String token) {
    return !isTokenExpired(token) && resignTokenMemory.check(token);
}

另一件事,您的令牌仅在 28.8 秒内有效,为了测试存在的理由,我建议您 expiraiton * 1000