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

网友投稿 466 2022-10-30


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

1 简介

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

2 项目整合

引入必要的依赖:

org.springframework.boot

spring-boot-starter-webflux

org.springframework.boot

spring-boot-starter-security

io.jsonwebtoken

jjwt

0.9.1

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 = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);

if (claims.getBody().getExpirationhttp://().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 ";

prihttp://vate final JwtTokenProvider tokenProvider;

public JwtTokenAuthenticationFilter(JwtTokenProvider tokenProvider) {

this.tokenProvider = tokenProvider;

}

@Override

public Mono 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 login(@RequestBody AuthRequest request) {

String username = request.getUsername();

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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:单片机上的发光二极管(LED灯)
下一篇:C言语次序查找算法及代码
相关文章

 发表评论

暂时没有评论,来抢沙发吧~