信息发布→ 登录 注册 退出

Springboot WebFlux集成Spring Security实现JWT认证的示例

发布时间:2026-01-11

点击量:
目录
  • 1 简介
  • 2 项目整合
    • 2.1 JWT工具类
    • 2.2 JWT的过滤器
    • 2.3 Security的配置
    • 2.4 获取JWT的Controller
  • 3 总结

    1 简介

    在之前的文章《Springboot集成Spring Security实现JWT认证》讲解了如何在传统的Web项目中整合Spring Security和JWT,今天我们讲解如何在响应式WebFlux项目中整合。二者大体是相同的,主要区别在于Reactive WebFlux与传统Web的区别。

    2 项目整合

    引入必要的依赖:

    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt</artifactId>
     <version>0.9.1</version>
    </dependency>

    2.1 JWT工具类

    该工具类主要功能是创建、校验、解析JWT。

    @Component
    public class JwtTokenProvider {
    
      private static final String AUTHORITIES_KEY = "roles";
    
      private final JwtProperties jwtProperties;
    
      private String secretKey;
    
      public JwtTokenProvider(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
      }
    
      @PostConstruct
      public void init() {
        secretKey = Base64.getEncoder().encodeToString(jwtProperties.getSecretKey().getBytes());
      }
    
      public String createToken(Authentication authentication) {
    
        String username = authentication.getName();
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        Claims claims = Jwts.claims().setSubject(username);
        if (!authorities.isEmpty()) {
          claims.put(AUTHORITIES_KEY, authorities.stream().map(GrantedAuthority::getAuthority).collect(joining(",")));
        }
    
        Date now = new Date();
        Date validity = new Date(now.getTime() + this.jwtProperties.getValidityInMs());
    
        return Jwts.builder()
            .setClaims(claims)
            .setIssuedAt(now)
            .setExpiration(validity)
            .signWith(SignatureAlgorithm.HS256, this.secretKey)
            .compact();
    
      }
    
      public Authentication getAuthentication(String token) {
        Claims claims = Jwts.parser().setSigningKey(this.secretKey).parseClaimsJws(token).getBody();
    
        Object authoritiesClaim = claims.get(AUTHORITIES_KEY);
    
        Collection<? extends GrantedAuthority> authorities = authoritiesClaim == null ? AuthorityUtils.NO_AUTHORITIES
            : AuthorityUtils.commaSeparatedStringToAuthorityList(authoritiesClaim.toString());
    
        User principal = new User(claims.getSubject(), "", authorities);
    
        return new UsernamePasswordAuthenticationToken(principal, token, authorities);
      }
    
      public boolean validateToken(String token) {
        try {
          Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
    
          if (claims.getBody().getExpiration().before(new Date())) {
            return false;
          }
    
          return true;
        } catch (JwtException | IllegalArgumentException e) {
          throw new InvalidJwtAuthenticationException("Expired or invalid JWT token");
        }
      }
    
    }

    2.2 JWT的过滤器

    这个过滤器的主要功能是从请求中获取JWT,然后进行校验,如何成功则把Authentication放进ReactiveSecurityContext里去。当然,如果没有带相关的请求头,那可能是通过其它方式进行鉴权,则直接放过,让它进入下一个Filter。

    public class JwtTokenAuthenticationFilter implements WebFilter {
    
      public static final String HEADER_PREFIX = "Bearer ";
    
      private final JwtTokenProvider tokenProvider;
    
      public JwtTokenAuthenticationFilter(JwtTokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
      }
    
      @Override
      public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String token = resolveToken(exchange.getRequest());
        if (StringUtils.hasText(token) && this.tokenProvider.validateToken(token)) {
          Authentication authentication = this.tokenProvider.getAuthentication(token);
          return chain.filter(exchange)
              .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication));
        }
        return chain.filter(exchange);
      }
    
      private String resolveToken(ServerHttpRequest request) {
        String bearerToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(HEADER_PREFIX)) {
          return bearerToken.substring(7);
        }
        return null;
      }
    }

    2.3 Security的配置

    这里设置了两个异常处理authenticationEntryPoint和accessDeniedHandler。

    @Configuration
    public class SecurityConfig {
    
      @Bean
      SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http,
                            JwtTokenProvider tokenProvider,
                            ReactiveAuthenticationManager reactiveAuthenticationManager) {
    
        return http.csrf(ServerHttpSecurity.CsrfSpec::disable)
            .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
            .authenticationManager(reactiveAuthenticationManager)
            .exceptionHandling().authenticationEntryPoint(
                (swe, e) -> {
          swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
          return swe.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap("UNAUTHORIZED".getBytes())));
        })
            .accessDeniedHandler((swe, e) -> {
          swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
          return swe.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap("FORBIDDEN".getBytes())));
        }).and()
            .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
            .authorizeExchange(it -> it
                .pathMatchers(HttpMethod.POST, "/auth/login").permitAll()
                .pathMatchers(HttpMethod.GET, "/admin").hasRole("ADMIN")
                .pathMatchers(HttpMethod.GET, "/user").hasRole("USER")
                .anyExchange().permitAll()
            )
            .addFilterAt(new JwtTokenAuthenticationFilter(tokenProvider), SecurityWebFiltersOrder.HTTP_BASIC)
            .build();
      }
    
    
      @Bean
      public ReactiveAuthenticationManager reactiveAuthenticationManager(CustomUserDetailsService userDetailsService,
                                        PasswordEncoder passwordEncoder) {
        UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
        authenticationManager.setPasswordEncoder(passwordEncoder);
        return authenticationManager;
      }
    }

    2.4 获取JWT的Controller

    先判断对用户密码进行判断,如果正确则返回对应的权限用户,根据用户生成JWT,再返回给客户端。

    @RestController
    @RequestMapping("/auth")
    public class AuthController {
    
      @Autowired
      ReactiveAuthenticationManager authenticationManager;
    
      @Autowired
      JwtTokenProvider jwtTokenProvider;
    
      @PostMapping("/login")
      public Mono<String> login(@RequestBody AuthRequest request) {
        String username = request.getUsername();
        Mono<Authentication> authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, request.getPassword()));
    
        return authentication.map(auth -> jwtTokenProvider.createToken(auth));
      }
    }

    3 总结

    其它与之前的大同小异,不一一讲解了。

    代码请查看:https://github.com/LarryDpk/pkslow-samples

    以上就是Springboot WebFlux集成Spring Security实现JWT认证的示例的详细内容,更多关于Springboot WebFlux集成Spring Security的资料请关注其它相关文章!

    在线客服
    服务热线

    服务热线

    4008888355

    微信咨询
    二维码
    返回顶部
    ×二维码

    截屏,微信识别二维码

    打开微信

    微信号已复制,请打开微信添加咨询详情!