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

下面是关于“Springboot WebFlux集成Spring Security实现JWT认证的示例”的完整攻略。

一、简介

在开始之前,先简单介绍一下SpringBoot和SpringSecurity。

SpringBoot:是Spring官方提供的一个快速开发框架,它能够极大地简化项目的搭建和配置,提高开发效率。

SpringSecurity:是Spring官方提供的一个安全框架,用于实现基于角色的授权和身份验证等安全功能。

本文将结合WebFlux和SpringSecurity,实现JWT认证功能的示例。

二、技术栈

  • SpringBoot 2.0+
  • Spring Security 5.0+
  • WebFlux
  • JWT

三、使用步骤

1.添加相关依赖:

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

2.实现登录验证逻辑

首先实现一个类来封装用户登录信息:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class LoginDTO {
    private String username;
    private String password;
}

然后,创建登录接口/api/login,用于接收用户的登录信息,并进行身份验证:

@RestController
@RequestMapping("/api")
public class LoginController {

    @PostMapping("/login")
    public Mono<ResponseEntity<?>> login(@RequestBody LoginDTO loginDTO) {

        //TODO: 1.进行身份认证
        //TODO: 2.生成JWT并返回

        return Mono.empty();
    }
}

在进行身份认证之前我们需要实现自定义的UserDetailsService接口,并重写其中的loadUserByUsername方法,用于从数据库或其他地方查询用户的详细信息。

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public Mono<UserDetails> findByUsername(String username) {
        return userRepository.findByUsername(username)
                .map(user -> User.withUsername(user.getUsername())
                        .password(user.getPassword())
                        .roles(user.getRoles())
                        .build())
                .cast(UserDetails.class);
    }
}

在上面实现的方式中,UserRepository是一个自定义的接口,用于查询用户名和密码等信息。

@Repository
public interface UserRepository {

    Mono<User> findByUsername(String username);

}

接下来实现登录逻辑:

@Autowired
private AuthenticationManager authenticationManager;

@Autowired
private JwtConfig jwtConfig;

@Autowired
private UserDetailsService userDetailsService;

@PostMapping("/login")
public Mono<ResponseEntity<?>> login(@RequestBody LoginDTO loginDTO) {
    return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginDTO.getUsername(), loginDTO.getPassword()))
            .map(authentication -> {
                String token = jwtConfig.generateToken((UserDetails) authentication.getPrincipal());
                return ResponseEntity.ok(Collections.singletonMap("token", token));
            })
            .defaultIfEmpty(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
}

登录过程中通过调用AuthenticationManagerauthenticate方法进行身份验证,并在验证成功后,生成Token返回给客户端。

这里通过JwtConfig类,实现了JWT Token的生成和验证逻辑,其中generateToken方法用于生成Token,validateToken方法用于验证Token。

3.实现Token拦截器

添加一个Token拦截器来验证用户提交的Token是否合法,拦截器相关代码如下:

public class JwtTokenAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtConfig jwtConfig;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        try {
            String authorizationHeader = httpServletRequest.getHeader(jwtConfig.getHeader());

            if (StringUtils.isBlank(authorizationHeader) || !authorizationHeader.startsWith(jwtConfig.getTokenPrefix())) {
                filterChain.doFilter(httpServletRequest, httpServletResponse);
                return;
            }

            String token = authorizationHeader.replace(jwtConfig.getTokenPrefix(), "");

            if (StringUtils.isNotBlank(token) && jwtConfig.validateToken(token)) {
                Authentication authentication = new UsernamePasswordAuthenticationToken(jwtConfig.getUsername(token), null);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }

        } catch (Exception e) {
            logger.error("Failed to set user authentication in security context", e);
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

4.配置SpringSecurity

配置SpringSecurity,并指定自定义的UserDetailsService。

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Autowired
    private JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter;

    @Autowired
    private JwtConfig jwtConfig;

    @Bean
    public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
        return http
                .authorizeExchange()
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                .pathMatchers("/**").hasRole("USER")
                .and()
                .csrf().disable()
                .httpBasic().disable()
                .formLogin().disable()
                .addFilterBefore(jwtTokenAuthenticationFilter, SecurityWebFiltersOrder.HTTP_BASIC)
                .build();
    }

    @Bean
    public ReactiveUserDetailsService reactiveUserDetailsService() {
        return username -> jwtConfig.getUserDetails(username);
    }
}

5.配置JwtConfig

配置JwtConfig,实现Token的生成和验证逻辑:

@Data
@ConfigurationProperties(prefix = "jwt")
public class JwtConfig {

    private String secret;
    private String tokenPrefix;
    private String header;

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();

        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    public Boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (SignatureException e) {
            logger.error("Invalid JWT signature -> {}", e.getMessage());
        } catch (MalformedJwtException e) {
            logger.error("Invalid JWT token -> {}", e.getMessage());
        } catch (ExpiredJwtException e) {
            logger.error("Expired JWT token -> {}", e.getMessage());
        } catch (UnsupportedJwtException e) {
            logger.error("Unsupported JWT token -> {}", e.getMessage());
        } catch (IllegalArgumentException e) {
            logger.error("JWT claims string is empty -> {}", e.getMessage());
        }
        return false;
    }

    public String getUsername(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
    }

    public UserDetails getUserDetails(String username) {
        return new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
    }
}

四、示例

这里提供两个参考示例:

示例一

基于SpringBoot的WebFlux和SpringSecurity实现JWT Token认证,Github:https://github.com/pkpk1234/springboot-webflux-websocket/tree/springboot-webflux-security-jwt-token

示例二

基于SpringBoot的WebFlux和SpringSecurity实现JWT Token认证,并且结合了实时多人聊天的场景,Github:https://github.com/WebCodeDream/spring-webflux-websocket-jwt-chatroom

通过这两个示例,可以更深入地理解SpringBoot WebFlux集成Spring Security实现JWT认证的流程和各组件之间的相互关系。

希望对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Springboot WebFlux集成Spring Security实现JWT认证的示例 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Spring Boot启动及退出加载项的方法

    一、SpringBoot启动及退出加载项的方法 SpringBoot是Spring开发的一款快速应用开发框架,其内置了很多工具和插件,可以让我们非常方便地进行开发。当我们启动SpringBoot应用时,会默认加载一些列的启动项,而这些启动项实际上也是可以自定义的。同样地,当我们停止SpringBoot应用时,也会默认执行一些列的退出项,这些退出项也同样是可以…

    Java 2023年5月15日
    00
  • java模拟多线程http请求代码分享

    下面是详细的“java模拟多线程http请求代码分享”的攻略: 1. 如何使用Java模拟http请求 Java模拟http请求的主要方式是通过Java的第三方库Apache HttpClient进行实现。下面是几个常用的示例:1. 发送POST请求: CloseableHttpClient httpClient = HttpClients.createDe…

    Java 2023年5月18日
    00
  • java中String,数组,ArrayList三者之间的转换

    对于Java中的String、数组和ArrayList,它们之间的转换是非常常见和实用的操作。下面我将为您提供一份完整攻略: 1. String转数组 将一个字符串转换成字符数组非常简单,只需要使用 String 类的 toCharArray() 方法即可。例如: String str = "Hello, world!"; char[] …

    Java 2023年5月26日
    00
  • 详解Java String字符串获取每一个字符及常用方法

    详解Java String字符串获取每一个字符及常用方法 获取每一个字符 在Java中,我们可以通过以下两种方式获取字符串中的每个字符: 1. 使用charArray方法 该方法将字符串转换为字符数组,然后遍历该数组即可获取每个字符。 示例代码如下: String str = "Hello World!"; char[] charArra…

    Java 2023年5月26日
    00
  • 每天学Java!一分钟了解JRE与JDK

    每天学Java!一分钟了解JRE与JDK JRE 是什么? JRE(Java Runtime Environment)是 Java 运行环境。它包含了 JVM(Java 虚拟机),Java 核心类库,支持运行 Java 程序所需的基础组件。 JDK 是什么? JDK(Java Development Kit)是 Java 开发工具包。它包含了所有开发 Jav…

    Java 2023年5月26日
    00
  • tomcat6下jsp出现getOutputStream() has already been called for this response异常的原因和解决方法

    下面是详细讲解“tomcat6下jsp出现getOutputStream() has already been called for this response异常的原因和解决方法”的完整攻略。 问题描述 在使用tomcat6运行jsp页面过程中,有可能会遇到getOutputStream() has already been called for this…

    Java 2023年6月2日
    00
  • 如何进行Java并发编程?

    下面是关于如何进行Java并发编程的完整使用攻略。 1. 理解Java的并发问题 在开始了解如何进行Java并发编程之前,首先我们需要对Java的并发问题进行了解。Java并发问题主要体现在多线程协同执行的过程中,比如线程间的互斥、同步、等待-通知机制等。 2. Java中的并发编程工具 在Java中处理并发问题常用的工具包括线程、锁、Semaphore等。…

    Java 2023年5月11日
    00
  • SSH框架网上商城项目第2战之基本增删查改、Service和Action的抽取

    本文将详细讲解SSH框架网上商城项目第2战之基本增删查改、Service和Action的抽取的完整攻略,包括概述、步骤、示例等内容。 概述 SSH框架是指以Struts2作为Web层,Spring作为业务层和数据访问层的容器,Hibernate作为数据持久化层的开发框架。本次攻略的目的是介绍如何在SSH框架下实现基本增删查改操作,以及Service和Acti…

    Java 2023年6月15日
    00
合作推广
合作推广
分享本页
返回顶部