Springboot WebFlux集成Spring Security实现JWT认证
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶!
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集成Spring Security实现JWT认证
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 Spring Security作为成熟且强大的安全框架,得到许多大厂的青睐.而作为前后端分离的SSO方案,JWT ...
- spring boot rest 接口集成 spring security(2) - JWT配置
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- 「快学springboot」集成Spring Security实现鉴权功能
Spring Security介绍 Spring Security是Spring全家桶中的处理身份和权限问题的一员.Spring Security可以根据使用者的需要定制相关的角色身份和身份所具有的权 ...
- Spring Boot 集成 Spring Security 实现权限认证模块
作者:王帅@CodeSheep 写在前面 关于 Spring Security Web系统的认证和权限模块也算是一个系统的基础设施了,几乎任何的互联网服务都会涉及到这方面的要求.在Java EE领 ...
- spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)
最近再学习spring security oauth2.下载了官方的例子sparklr2和tonr2进行学习.但是例子里包含的东西太多,不知道最简单最主要的配置有哪些.所以决定自己尝试搭建简单版本的例 ...
- SpringBoot 集成Spring security
Spring security作为一种安全框架,使用简单,能够很轻松的集成到springboot项目中,下面讲一下如何在SpringBoot中集成Spring Security.使用gradle项目管 ...
- SpringBoot集成Spring Security
1.Spring Security介绍 Spring security,是一个强大的和高度可定制的身份验证和访问控制框架.它是确保基于Spring的应用程序的标准 --来自官方参考手册 Spring ...
- spring boot rest 接口集成 spring security(1) - 最简配置
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- SpringBoot集成Spring Security(7)——认证流程
文章目录 一.认证流程 二.多个请求共享认证信息 三.获取用户认证信息 在前面的六章中,介绍了 Spring Security 的基础使用,在继续深入向下的学习前,有必要理解清楚 Spring Sec ...
随机推荐
- C++逆向分析----虚函数与多层继承
虚函数 C++通过关键字virtual来将函数声明为一个虚函数.当一个类包含虚函数后编译器就会给类的实例对象增加一个虚表指针,所谓的虚表指针就是指向虚表的指针.虚表就是一张地址表,它包含了这个类中所有 ...
- [bug] Navicat 连 虚拟机MySQL
参考 https://www.cnblogs.com/brankoliu/p/10845491.html https://blog.csdn.net/qq_40087740/article/detai ...
- [Python] 条件 & 循环
条件语句 不加 () 结尾加 : elif else 和 if 成对使用 省略判断条件 String:空字符串为False,其余为True int:0为False,其余为True Bool:True为 ...
- [前端] AJAX
背景 Asynchronous JavaScript And XML:异步js和XML,可实现异步刷新 用途 验证提交的用户名是否已存在 不使用AJAX,需要提交数据后,刷新页面来验证 使用AJAX, ...
- kubernetes架构及deployment应用(4)
Kubernetes Cluster 由 Master 和 Node 组成,节点上运行着若干 Kubernetes 服务. 一.master节点 Master 是 Kubernetes Cluster ...
- 045.Python线程队列
线程队列 1 基本语法和用法 put 往线程队列里防止,超过队列长度,直接阻塞 get 从队列中取值,如果获取不到,直接阻塞 put_nowait: 如果放入的值超过队列长度,直接报错(linux) ...
- Linux中级之keepalived配置
hacmp: ibm的高可用集群软件,并且是商业的(收费),一般用于非x86架构机器当中 AIX,Unix 去IOE:ibm,oracle,emckeepalived: 一款高可用集群软件,利用vrr ...
- C++对象内存分布详解(包括字节对齐和虚函数表)
转自:https://www.jb51.net/article/101122.htm 1.C++对象的内存分布和虚函数表: C++对象的内存分布和虚函数表注意,对象中保存的是虚函数表指针,而不是虚函数 ...
- Django中数据库操作相关的错误
问题:字段修改属性发生错误 1> >python manage.py makemigrations You are trying to add a non-nullable field ' ...
- Java基础之第二章变量
1. 变量介绍 变量是程序的基本组成单位 概念 变量相当于内存中一个数据存储空间的表示,可以通过变量名可以访问到变量(值). 变量使用 声明变量 int a; 赋值 a = 20; public cl ...