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

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的资料请关注我们其它相关文章!

(0)

相关推荐

  • 解决spring-boot2.0.6中webflux无法获得请求IP的问题

    这几天在用 spring-boot 2 的 webflux 重构一个工程,写到了一个需要获得客户端请求 IP 的地方,发现写不下去了,在如下的 Handler(webflux 中 Handler 相当于 mvc 中的 Controller)中 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; im

  • 聊聊spring boot的WebFluxTagsProvider的使用

    序 本文主要研究一下webflux的WebFluxTagsProvider WebFluxTagsProvider spring-boot-actuator-2.1.5.RELEASE-sources.jar!/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTagsProvider.java @FunctionalInterface public interface WebFluxTagsProvider

  • 使用 Spring Boot 2.0 + WebFlux 实现 RESTful API功能

    概述 什么是 Spring WebFlux, 它是一种异步的, 非阻塞的, 支持背压(Back pressure)机制的Web 开发框架. 要深入了解 Spring WebFlux, 首先要了知道 Reactive Stream . 另一种编程姿势, 和命令式编程相对的姿势. WebFlux 支持两种编程风(姿)格(势) 使用 @Controller 这种基于注解的姿势, 与Sring MVC的姿势相同 基于Java 8 Lambda的函数式编程风格 注意: 上面只是两种编程的姿势, 和"普通话

  • 如何在Spring WebFlux的任何地方获取Request对象

    1 不一样的世界 在常规的Spring Web项目中,我们要获取Request对象是非常方便的,不少库都提供了静态方法来获取.获取代码如下: ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); // get the request HttpServletRequest request = requestAttribut

  • Spring Boot webflux使用方法解析

    1.同步阻塞IO模型 当容器中只有三个线程接收请求,当有四个请求过来的时候,就会Block住,得不到及时的响应 2.异步非阻塞式IO模型 Spring Boot webflux是异步非阻塞式IO模型,容器线程将耗时的任务(IO密集型任务)交给work线程来处理 3.webflux应用场景 4.webflux与springmvc异同点 5.webflux使用建议 1).如果当前项目比较稳定,没必要切换.如果要切换最好切换整套技术栈 2).如果只是个人对新技术感兴趣,可以在一些简单小型项目中使用研究

  • 详解Spring Boot2 Webflux的全局异常处理

    本文首先将会回顾Spring 5之前的SpringMVC异常处理机制,然后主要讲解Spring Boot 2 Webflux的全局异常处理机制. SpringMVC的异常处理 Spring 统一异常处理有 3 种方式,分别为: 使用 @ExceptionHandler 注解 实现 HandlerExceptionResolver 接口 使用 @controlleradvice 注解 使用 @ExceptionHandler 注解 用于局部方法捕获,与抛出异常的方法处于同一个Controller类

  • Spring WebFlux的使用指南

    Spring WebFlux是spring5的一部分,它为web应用程序提供反应式编程支持. 在本教程中,我们将使用RestController和WebClient创建一个小型响应式REST应用程序. 我们还将研究如何使用Spring安全保护我们的反应端点. Spring-WebFlux框架 Spring WebFlux在内部使用Project Reactor及其发布者实现Flux和Mono. 新框架支持两种编程模型: 基于注释的反应元件 功能路由和处理 依赖项 让我们从spring boot

  • SpringBoot2使用WebFlux函数式编程的方法

    本文只是简单使用SpringBoot2使用WebFlux的函数式编程简单使用,后续会继续写关于Webflux相关的文章. 最近一直在研究WebFlux,后续会陆续出一些相关的文章. 首先看一下Srping官网上的一张图,对比一下SpringMvc和Spring WebFlux,如图: 在查看一下WebFlux的官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html,W

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

    1 简介 在之前的文章<Springboot集成Spring Security实现JWT认证>讲解了如何在传统的Web项目中整合Spring Security和JWT,今天我们讲解如何在响应式WebFlux项目中整合.二者大体是相同的,主要区别在于Reactive WebFlux与传统Web的区别. 2 项目整合 引入必要的依赖: <dependency> <groupId>org.springframework.boot</groupId> <art

  • Springboot集成Spring Security实现JWT认证的步骤详解

    1 简介 Spring Security作为成熟且强大的安全框架,得到许多大厂的青睐.而作为前后端分离的SSO方案,JWT也在许多项目中应用.本文将介绍如何通过Spring Security实现JWT认证. 用户与服务器交互大概如下: 客户端获取JWT,一般通过POST方法把用户名/密码传给server: 服务端接收到客户端的请求后,会检验用户名/密码是否正确,如果正确则生成JWT并返回:不正确则返回错误: 客户端拿到JWT后,在有效期内都可以通过JWT来访问资源了,一般把JWT放在请求头:一次

  • SpringBoot集成Spring Security用JWT令牌实现登录和鉴权的方法

    最近在做项目的过程中 需要用JWT做登录和鉴权 查了很多资料 都不甚详细 有的是需要在application.yml里进行jwt的配置 但我在导包后并没有相应的配置项 因而并不适用 在踩过很多坑之后 稍微整理了一下 做个笔记 一.概念 1.什么是JWT Json Web Token (JWT)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519) 该token被设计为紧凑且安全的 特别适用于分布式站点的单点登录(SSO)场景 随着JWT的出现 使得校验方式更加简单便

  • SpringBoot集成Spring Security的方法

    至今Java能够如此的火爆Spring做出了很大的贡献,它的出现让Java程序的编写更为简单灵活,而Spring如今也形成了自己的生态圈,今天咱们探讨的是Spring旗下的一个款认证工具:SpringSecurity,如今认证框架主流"shiro"和"SpringSecurity",由于和Spring的无缝衔接,使用SpringSecurity的企业也越来越多. 1.Spring Security介绍 Spring security,是一个强大的和高度可定制的身份验

  • SpringBoot集成Spring security JWT实现接口权限认证

    1.添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjw

  • 详解Springboot2.3集成Spring security 框架(原生集成)

    0.pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0

  • Spring Security中用JWT退出登录时遇到的坑

    最近有个粉丝提了个问题,说他在Spring Security中用JWT做退出登录的时无法获取当前用户,导致无法证明"我就是要退出的那个我",业务失败!经过我一番排查找到了原因,而且这个错误包括我自己的大部分人都犯过. Session会话 之所以要说Session会话,是因为Spring Security默认配置就是有会话的,所以当你登录以后Session就会由服务端保持直到你退出登录.只要Session保持住,你的请求只要进入服务器就可以从 ServletRequest 中获取到当前的

  • SpringBoot工程中Spring Security应用实践记录流程分析

    目录 SpringSecurity 应用 简介 认证授权分析 SpringSecurity 架构设计 快速入门实践 创建项目 添加项目依赖 启动服务访问测试 自定义认证逻辑 认证流程分析 定义security配置类 定义数据访问层对象 定义UserDetailService接口实现类 自定义登陆页面 启动服务进行访问测试 授权逻辑设计及实现 修改授权配置类 定义资源访问对象 启动服务实现访问测试 总结(Summary) SpringSecurity 应用 简介 Spring Security是一

  • Spring Security实现HTTP认证

    目录 前言 一.HTTP基本认证是什么? 二.HTTP基本认证流程 一.Spring Security使用HTTP基本认证 1.创建项目spring-security-http-auth 2.创建配置文件WebSecurityConfig 3.运行项目 二.Spring Security HTTP基本认证原理 三.HTTP摘要认证是什么? 四.Spring Security使用HTTP摘要认证流程? 运行项目 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安

  • Spring Security使用数据库认证及用户密码加密和解密功能

    流程图: 1.接上一篇博客https://mp.csdn.net/console/editor/html/104576494,准备好环境. 2.spring-security.xml中的配置: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="

随机推荐