共计 5687 个字符,预计需要花费 15 分钟才能阅读完成。
我最新最全的文章都在 南瓜慢说 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/p…
欢送关注微信公众号 <南瓜慢说>,将继续为你更新 …
多读书,多分享;多写作,多整顿。
正文完