Webflux JWT授权无法正常工作

时间:2019-06-30 15:24:53

标签: spring security spring-security jwt spring-webflux

我是在春季反应性环境(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();


    }

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

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

enter image description here

但是尝试注销(由于我只是在学习,所以我实现了自我调整,使其处于全状态)。

这是我的注销控制器:


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

}

注销请求如下:

enter image description here enter image description here

如上图所示,我相信我做的不错,但是我收到了错误日志消息:

  

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

}

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

1 个答案:

答案 0 :(得分:2)

写作上有区别

//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),并且如果您调用JWTReactiveAuthenticationManager,则先前的主题会丢失!这个JWT生成器是伪造的。

否则,您在import org.springframework.context.support.BeanDefinitionDsl.Role; 中导入了错误的Role类,则必须替换:

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

作者

validateToken()

最后但并非最不重要的是,由于falsecheck(token)将始终返回putput呼叫来不及了,您必须意识到这一点。在调用check方法之前,要么删除此检查,要么移动resignTokenMemory执行。

我不确定您要使用public Boolean validateToken(String token) { return !isTokenExpired(token) && resignTokenMemory.check(token); } 做什么,所以我将让您自行修复:

expiraiton * 1000

另一件事,您的令牌仅在28.8秒内有效,我建议您[routerLink]来测试存在性。