首页 > 解决方案 > Webflux JWT 授权无法正常工作

问题描述

我正在学习关于 Spring 响应式上下文 (webflux) 中的 JWT的教程。

Authorization令牌生成工作正常,但是当我使用with时授权不起作用bearer

这是我所做的:

@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 用户尝试访问未经授权的 api 端点:/auth/logout 中的 /127.0.0.1:45776

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


/**
 * 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.");
    }

}

我无法看到或发现我犯的任何问题或错误。错误在哪里?

标签: springsecurityspring-securityjwtspring-webflux

解决方案


写法有区别

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

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

确实,看看类中setSubject的方法DefaultJwtBuilder

@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 构建器是假的。

否则,您在 中导入了错误的 Role 类JWTReactiveAuthenticationManager,您必须替换:

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

经过

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

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

我不确定你想做什么resignTokenMemory,所以我会让你自己修复它:

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

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


推荐阅读