时间:2021-05-20
在之前的文章《Springboot集成Spring Security实现JWT认证》讲解了如何在传统的Web项目中整合Spring Security和JWT,今天我们讲解如何在响应式WebFlux项目中整合。二者大体是相同的,主要区别在于Reactive WebFlux与传统Web的区别。
引入必要的依赖:
<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>该工具类主要功能是创建、校验、解析JWT。
@Componentpublic 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"); } }}这个过滤器的主要功能是从请求中获取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; }}这里设置了两个异常处理authenticationEntryPoint和accessDeniedHandler。
@Configurationpublic 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; }}先判断对用户密码进行判断,如果正确则返回对应的权限用户,根据用户生成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)); }}其它与之前的大同小异,不一一讲解了。
代码请查看:https://github.com/LarryDpk/pkslow-samples
以上就是Springboot WebFlux集成Spring Security实现JWT认证的示例的详细内容,更多关于Springboot WebFlux集成Spring Security的资料请关注其它相关文章!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本文介绍了springBoot实战之Filter实现使用JWT进行接口认证,分享给大家jwt(jsonwebtoken)用户发送按照约定,向服务端发送Heade
在SpringBoot中集成Shiro,并使用JWT进行接口认证。为了统一对Token进行过滤,所以自定义了一个JwtTokenFilter过滤器。期间遇到了以
导入依赖(pom.xml)org.apache.shiroshiro-spring1.4.0com.auth0java-jwt3.2.0在SpringBoot项
在本教程中,我们将研究标准Spring框架和SpringBoot之间的区别。我们将重点讨论Spring的模块,如MVC和Security,在核心Spring中使
spring-security里自带了oauth2,正好YIIU里也用到了spring-security做权限部分,那为何不直接集成上第三方登录呢?然后我开始了