1 简介
在之前的文章《 Springboot集成Spring Security实现JWT认证 》讲解了如何在传统的Web项目中整合Spring Security和JWT,今天我们讲解如何在响应式WebFlux项目中整合。二者大体是相同的,主要区别在于Reactive WebFlux与传统Web的区别。
2 项目整合
引入必要的依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
< 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
@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) 测试数据pact();
}
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测试数据maSeparatedStringToAuthorityList(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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
@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,再返回给客户端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@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测试数据/LarryDpk/pkslow-samples
以上就是Springboot WebFlux集成Spring Security实现JWT认证的示例的详细内容,更多关于Springboot WebFlux集成Spring Security的资料请关注其它相关文章!
原文链接:https://HdhCmsTestpkslow测试数据/archives/springboot-spring-security-jwt-webflux
查看更多关于Springboot WebFlux集成Spring Security实现JWT认证的示例的详细内容...