Spring中的aware接口详情
476
2022-10-30
Springboot WebFlux集成Spring Security实现JWT认证的示例
1 简介
在之前的文章《Springboot集成Spring Security实现JWT认证》讲解了如何在传统的Web项目中整合Spring Security和JWT,今天我们讲解如何在响应式WebFlux项目中整合。二者大体是相同的,主要区别在于Reactive WebFlux与传统Web的区别。
2 项目整合
引入必要的依赖:
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
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
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 username = request.getUsername();
Mono
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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~