简体   繁体   中英

Webflux JWT Authorization not working fine

I am following a tutorial about JWT in a spring reactive context (webflux).

The token generation is working fine, however the authorization is not working when I use the Authorization with bearer

Here is what I have done:

@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();


    }

As you can see, I want to simply deny all not authorized requests other than login or options based ones.

The login is working fine and I'm getting a token.

在此处输入图片说明

But trying to logout (a tweak that I implemented my self to make it state-full since I m only learning) is not working.

Here is my logout controller:


@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);
    }

}

The logout request is as below:

在此处输入图片说明 在此处输入图片说明

As stated in images above, I believe that I m doing fine, however I m getting the error log message:

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

Here is my security context content:


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

}

I'm unable to see or find any issue or error that I have made. Where is the mistake?

There's a difference in writing

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

and

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

Indeed, look at setSubject method in the DefaultJwtBuilder class :

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

When setSubject(username) is called first, ensureClaims() creates a DefaultClaims without yours and if you call setClaims(claims) the precedent subject is lost ! This JWT builder is bogus.

Otherwise, you're importing the wrong Role class in JWTReactiveAuthenticationManager , you have to replace :

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

by

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

Last and not least, validateToken() will return always false because of the check(token) . put call is coming too late, you have to be aware of that. Either you remove this check or you move the put execution before calling the check method.

I'am not sure about what you want to do with resignTokenMemory , so i'll let you fix it by your own:

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

Another thing, your token is valid only 28,8 second, for testing raison i recommend you to expiraiton * 1000 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM